diff --git a/.gdbinit.tmpl-i386 b/.gdbinit.tmpl-i386 index f4f85d2..a3a274b 100644 --- a/.gdbinit.tmpl-i386 +++ b/.gdbinit.tmpl-i386 @@ -1,3 +1,4 @@ +set confirm off python gdb.execute("target remote localhost:26000") gdb.execute("set architecture i386") diff --git a/.gdbinit.tmpl-riscv b/.gdbinit.tmpl-riscv index 6ea36e1..c1616b6 100644 --- a/.gdbinit.tmpl-riscv +++ b/.gdbinit.tmpl-riscv @@ -1,3 +1,4 @@ +set confirm off set architecture riscv target remote 127.0.0.1:1234 symbol-file kernel diff --git a/Makefile b/Makefile index 84355e0..38ec5c7 100644 --- a/Makefile +++ b/Makefile @@ -187,13 +187,13 @@ endif QEMUOPTS = -machine virt -kernel kernel -m 3G -smp $(CPUS) -nographic QEMUOPTS += -initrd fs.img -qemu: kernel +qemu: kernel fs.img $(QEMU) $(QEMUOPTS) .gdbinit: .gdbinit.tmpl-riscv sed "s/:1234/:$(GDBPORT)/" < $^ > $@ -qemu-gdb: kernel .gdbinit +qemu-gdb: kernel .gdbinit fs.img @echo "*** Now run 'gdb'." 1>&2 $(QEMU) $(QEMUOPTS) -S $(QEMUGDB) diff --git a/asm.h b/asm.h deleted file mode 100644 index b8a7353..0000000 --- a/asm.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// assembler macros to create x86 segments -// - -#define SEG_NULLASM \ - .word 0, 0; \ - .byte 0, 0, 0, 0 - -// The 0xC0 means the limit is in 4096-byte units -// and (for executable segments) 32-bit mode. -#define SEG_ASM(type,base,lim) \ - .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ - .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ - (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) - -#define STA_X 0x8 // Executable segment -#define STA_W 0x2 // Writeable (non-executable segments) -#define STA_R 0x2 // Readable (executable segments) diff --git a/bootmain.c b/bootmain.c deleted file mode 100644 index 1f20e5b..0000000 --- a/bootmain.c +++ /dev/null @@ -1,96 +0,0 @@ -// Boot loader. -// -// Part of the boot block, along with bootasm.S, which calls bootmain(). -// bootasm.S has put the processor into protected 32-bit mode. -// bootmain() loads an ELF kernel image from the disk starting at -// sector 1 and then jumps to the kernel entry routine. - -#include "types.h" -#include "elf.h" -#include "x86.h" -#include "memlayout.h" - -#define SECTSIZE 512 - -void readseg(uchar*, uint, uint); - -void -bootmain(void) -{ - struct elfhdr *elf; - struct proghdr *ph, *eph; - void (*entry)(void); - uchar* pa; - - elf = (struct elfhdr*)0x10000; // scratch space - - // Read 1st page off disk - readseg((uchar*)elf, 4096, 0); - - // Is this an ELF executable? - if(elf->magic != ELF_MAGIC) - return; // let bootasm.S handle error - - // Load each program segment (ignores ph flags). - ph = (struct proghdr*)((uchar*)elf + elf->phoff); - eph = ph + elf->phnum; - for(; ph < eph; ph++){ - pa = (uchar*)ph->paddr; - readseg(pa, ph->filesz, ph->off); - if(ph->memsz > ph->filesz) - stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz); - } - - // Call the entry point from the ELF header. - // Does not return! - entry = (void(*)(void))(elf->entry); - entry(); -} - -void -waitdisk(void) -{ - // Wait for disk ready. - while((inb(0x1F7) & 0xC0) != 0x40) - ; -} - -// Read a single sector at offset into dst. -void -readsect(void *dst, uint offset) -{ - // Issue command. - waitdisk(); - outb(0x1F2, 1); // count = 1 - outb(0x1F3, offset); - outb(0x1F4, offset >> 8); - outb(0x1F5, offset >> 16); - outb(0x1F6, (offset >> 24) | 0xE0); - outb(0x1F7, 0x20); // cmd 0x20 - read sectors - - // Read data. - waitdisk(); - insl(0x1F0, dst, SECTSIZE/4); -} - -// Read 'count' bytes at 'offset' from kernel into physical address 'pa'. -// Might copy more than asked. -void -readseg(uchar* pa, uint count, uint offset) -{ - uchar* epa; - - epa = pa + count; - - // Round down to sector boundary. - pa -= offset % SECTSIZE; - - // Translate from bytes to sectors; kernel starts at sector 1. - offset = (offset / SECTSIZE) + 1; - - // If this is too slow, we could read lots of sectors at a time. - // We'd write more to memory than asked, but it doesn't matter -- - // we load in increasing order. - for(; pa < epa; pa += SECTSIZE, offset++) - readsect(pa, offset); -} diff --git a/entry.S b/entry.S index 8b3316c..b3d2c55 100644 --- a/entry.S +++ b/entry.S @@ -10,12 +10,15 @@ .section .text .globl _entry _entry: - # set up a stack for C; stack0 is declared in start. + # set up a stack for C. + # stack0 is declared in start, + # with 4096 bytes per CPU. la sp, stack0 - addi sp, sp, 1024 - addi sp, sp, 1024 - addi sp, sp, 1024 - addi sp, sp, 1024 + li a0, 1024*4 + csrr a1, mhartid + addi a1, a1, 1 + mul a0, a0, a1 + add sp, sp, a0 # jump to mstart() in start.c call mstart junk: diff --git a/kbd.c b/kbd.c deleted file mode 100644 index 32c1463..0000000 --- a/kbd.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "types.h" -#include "x86.h" -#include "defs.h" -#include "kbd.h" - -int -kbdgetc(void) -{ - static uint shift; - static uchar *charcode[4] = { - normalmap, shiftmap, ctlmap, ctlmap - }; - uint st, data, c; - - st = inb(KBSTATP); - if((st & KBS_DIB) == 0) - return -1; - data = inb(KBDATAP); - - if(data == 0xE0){ - shift |= E0ESC; - return 0; - } else if(data & 0x80){ - // Key released - data = (shift & E0ESC ? data : data & 0x7F); - shift &= ~(shiftcode[data] | E0ESC); - return 0; - } else if(shift & E0ESC){ - // Last character was an E0 escape; or with 0x80 - data |= 0x80; - shift &= ~E0ESC; - } - - shift |= shiftcode[data]; - shift ^= togglecode[data]; - c = charcode[shift & (CTL | SHIFT)][data]; - if(shift & CAPSLOCK){ - if('a' <= c && c <= 'z') - c += 'A' - 'a'; - else if('A' <= c && c <= 'Z') - c += 'a' - 'A'; - } - return c; -} - -void -kbdintr(void) -{ - consoleintr(kbdgetc); -} diff --git a/kbd.h b/kbd.h deleted file mode 100644 index babbd6e..0000000 --- a/kbd.h +++ /dev/null @@ -1,112 +0,0 @@ -// PC keyboard interface constants - -#define KBSTATP 0x64 // kbd controller status port(I) -#define KBS_DIB 0x01 // kbd data in buffer -#define KBDATAP 0x60 // kbd data port(I) - -#define NO 0 - -#define SHIFT (1<<0) -#define CTL (1<<1) -#define ALT (1<<2) - -#define CAPSLOCK (1<<3) -#define NUMLOCK (1<<4) -#define SCROLLLOCK (1<<5) - -#define E0ESC (1<<6) - -// Special keycodes -#define KEY_HOME 0xE0 -#define KEY_END 0xE1 -#define KEY_UP 0xE2 -#define KEY_DN 0xE3 -#define KEY_LF 0xE4 -#define KEY_RT 0xE5 -#define KEY_PGUP 0xE6 -#define KEY_PGDN 0xE7 -#define KEY_INS 0xE8 -#define KEY_DEL 0xE9 - -// C('A') == Control-A -#define C(x) (x - '@') - -static uchar shiftcode[256] = -{ - [0x1D] CTL, - [0x2A] SHIFT, - [0x36] SHIFT, - [0x38] ALT, - [0x9D] CTL, - [0xB8] ALT -}; - -static uchar togglecode[256] = -{ - [0x3A] CAPSLOCK, - [0x45] NUMLOCK, - [0x46] SCROLLLOCK -}; - -static uchar normalmap[256] = -{ - NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00 - '7', '8', '9', '0', '-', '=', '\b', '\t', - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10 - 'o', 'p', '[', ']', '\n', NO, 'a', 's', - 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20 - '\'', '`', NO, '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30 - NO, ' ', NO, NO, NO, NO, NO, NO, - NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 - '8', '9', '-', '4', '5', '6', '+', '1', - '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 - [0x9C] '\n', // KP_Enter - [0xB5] '/', // KP_Div - [0xC8] KEY_UP, [0xD0] KEY_DN, - [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, - [0xCB] KEY_LF, [0xCD] KEY_RT, - [0x97] KEY_HOME, [0xCF] KEY_END, - [0xD2] KEY_INS, [0xD3] KEY_DEL -}; - -static uchar shiftmap[256] = -{ - NO, 033, '!', '@', '#', '$', '%', '^', // 0x00 - '&', '*', '(', ')', '_', '+', '\b', '\t', - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10 - 'O', 'P', '{', '}', '\n', NO, 'A', 'S', - 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20 - '"', '~', NO, '|', 'Z', 'X', 'C', 'V', - 'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30 - NO, ' ', NO, NO, NO, NO, NO, NO, - NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 - '8', '9', '-', '4', '5', '6', '+', '1', - '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 - [0x9C] '\n', // KP_Enter - [0xB5] '/', // KP_Div - [0xC8] KEY_UP, [0xD0] KEY_DN, - [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, - [0xCB] KEY_LF, [0xCD] KEY_RT, - [0x97] KEY_HOME, [0xCF] KEY_END, - [0xD2] KEY_INS, [0xD3] KEY_DEL -}; - -static uchar ctlmap[256] = -{ - NO, NO, NO, NO, NO, NO, NO, NO, - NO, NO, NO, NO, NO, NO, NO, NO, - C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'), - C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'), - C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO, - NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'), - C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO, - [0x9C] '\r', // KP_Enter - [0xB5] C('/'), // KP_Div - [0xC8] KEY_UP, [0xD0] KEY_DN, - [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, - [0xCB] KEY_LF, [0xCD] KEY_RT, - [0x97] KEY_HOME, [0xCF] KEY_END, - [0xD2] KEY_INS, [0xD3] KEY_DEL -}; - diff --git a/lapic.c b/lapic.c deleted file mode 100644 index b22bbd7..0000000 --- a/lapic.c +++ /dev/null @@ -1,229 +0,0 @@ -// The local APIC manages internal (non-I/O) interrupts. -// See Chapter 8 & Appendix C of Intel processor manual volume 3. - -#include "param.h" -#include "types.h" -#include "defs.h" -#include "date.h" -#include "memlayout.h" -#include "traps.h" -#include "mmu.h" -#include "x86.h" - -// Local APIC registers, divided by 4 for use as uint[] indices. -#define ID (0x0020/4) // ID -#define VER (0x0030/4) // Version -#define TPR (0x0080/4) // Task Priority -#define EOI (0x00B0/4) // EOI -#define SVR (0x00F0/4) // Spurious Interrupt Vector - #define ENABLE 0x00000100 // Unit Enable -#define ESR (0x0280/4) // Error Status -#define ICRLO (0x0300/4) // Interrupt Command - #define INIT 0x00000500 // INIT/RESET - #define STARTUP 0x00000600 // Startup IPI - #define DELIVS 0x00001000 // Delivery status - #define ASSERT 0x00004000 // Assert interrupt (vs deassert) - #define DEASSERT 0x00000000 - #define LEVEL 0x00008000 // Level triggered - #define BCAST 0x00080000 // Send to all APICs, including self. - #define BUSY 0x00001000 - #define FIXED 0x00000000 -#define ICRHI (0x0310/4) // Interrupt Command [63:32] -#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER) - #define X1 0x0000000B // divide counts by 1 - #define PERIODIC 0x00020000 // Periodic -#define PCINT (0x0340/4) // Performance Counter LVT -#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0) -#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1) -#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR) - #define MASKED 0x00010000 // Interrupt masked -#define TICR (0x0380/4) // Timer Initial Count -#define TCCR (0x0390/4) // Timer Current Count -#define TDCR (0x03E0/4) // Timer Divide Configuration - -volatile uint *lapic; // Initialized in mp.c - -//PAGEBREAK! -static void -lapicw(int index, int value) -{ - lapic[index] = value; - lapic[ID]; // wait for write to finish, by reading -} - -void -lapicinit(void) -{ - if(!lapic) - return; - - // Enable local APIC; set spurious interrupt vector. - lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS)); - - // The timer repeatedly counts down at bus frequency - // from lapic[TICR] and then issues an interrupt. - // If xv6 cared more about precise timekeeping, - // TICR would be calibrated using an external time source. - lapicw(TDCR, X1); - lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); - lapicw(TICR, 10000000); - - // Disable logical interrupt lines. - lapicw(LINT0, MASKED); - lapicw(LINT1, MASKED); - - // Disable performance counter overflow interrupts - // on machines that provide that interrupt entry. - if(((lapic[VER]>>16) & 0xFF) >= 4) - lapicw(PCINT, MASKED); - - // Map error interrupt to IRQ_ERROR. - lapicw(ERROR, T_IRQ0 + IRQ_ERROR); - - // Clear error status register (requires back-to-back writes). - lapicw(ESR, 0); - lapicw(ESR, 0); - - // Ack any outstanding interrupts. - lapicw(EOI, 0); - - // Send an Init Level De-Assert to synchronise arbitration ID's. - lapicw(ICRHI, 0); - lapicw(ICRLO, BCAST | INIT | LEVEL); - while(lapic[ICRLO] & DELIVS) - ; - - // Enable interrupts on the APIC (but not on the processor). - lapicw(TPR, 0); -} - -int -lapicid(void) -{ - if (!lapic) - return 0; - return lapic[ID] >> 24; -} - -// Acknowledge interrupt. -void -lapiceoi(void) -{ - if(lapic) - lapicw(EOI, 0); -} - -// Spin for a given number of microseconds. -// On real hardware would want to tune this dynamically. -void -microdelay(int us) -{ -} - -#define CMOS_PORT 0x70 -#define CMOS_RETURN 0x71 - -// Start additional processor running entry code at addr. -// See Appendix B of MultiProcessor Specification. -void -lapicstartap(uchar apicid, uint addr) -{ - int i; - ushort *wrv; - - // "The BSP must initialize CMOS shutdown code to 0AH - // and the warm reset vector (DWORD based at 40:67) to point at - // the AP startup code prior to the [universal startup algorithm]." - outb(CMOS_PORT, 0xF); // offset 0xF is shutdown code - outb(CMOS_PORT+1, 0x0A); - wrv = (ushort*)P2V((0x40<<4 | 0x67)); // Warm reset vector - wrv[0] = 0; - wrv[1] = addr >> 4; - - // "Universal startup algorithm." - // Send INIT (level-triggered) interrupt to reset other CPU. - lapicw(ICRHI, apicid<<24); - lapicw(ICRLO, INIT | LEVEL | ASSERT); - microdelay(200); - lapicw(ICRLO, INIT | LEVEL); - microdelay(100); // should be 10ms, but too slow in Bochs! - - // Send startup IPI (twice!) to enter code. - // Regular hardware is supposed to only accept a STARTUP - // when it is in the halted state due to an INIT. So the second - // should be ignored, but it is part of the official Intel algorithm. - // Bochs complains about the second one. Too bad for Bochs. - for(i = 0; i < 2; i++){ - lapicw(ICRHI, apicid<<24); - lapicw(ICRLO, STARTUP | (addr>>12)); - microdelay(200); - } -} - -#define CMOS_STATA 0x0a -#define CMOS_STATB 0x0b -#define CMOS_UIP (1 << 7) // RTC update in progress - -#define SECS 0x00 -#define MINS 0x02 -#define HOURS 0x04 -#define DAY 0x07 -#define MONTH 0x08 -#define YEAR 0x09 - -static uint -cmos_read(uint reg) -{ - outb(CMOS_PORT, reg); - microdelay(200); - - return inb(CMOS_RETURN); -} - -static void -fill_rtcdate(struct rtcdate *r) -{ - r->second = cmos_read(SECS); - r->minute = cmos_read(MINS); - r->hour = cmos_read(HOURS); - r->day = cmos_read(DAY); - r->month = cmos_read(MONTH); - r->year = cmos_read(YEAR); -} - -// qemu seems to use 24-hour GWT and the values are BCD encoded -void -cmostime(struct rtcdate *r) -{ - struct rtcdate t1, t2; - int sb, bcd; - - sb = cmos_read(CMOS_STATB); - - bcd = (sb & (1 << 2)) == 0; - - // make sure CMOS doesn't modify time while we read it - for(;;) { - fill_rtcdate(&t1); - if(cmos_read(CMOS_STATA) & CMOS_UIP) - continue; - fill_rtcdate(&t2); - if(memcmp(&t1, &t2, sizeof(t1)) == 0) - break; - } - - // convert - if(bcd) { -#define CONV(x) (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf)) - CONV(second); - CONV(minute); - CONV(hour ); - CONV(day ); - CONV(month ); - CONV(year ); -#undef CONV - } - - *r = t1; - r->year += 2000; -} diff --git a/main.c b/main.c index db9a6b9..24793cd 100644 --- a/main.c +++ b/main.c @@ -8,11 +8,12 @@ // Allocate a real stack and switch to it, first // doing some setup required for memory allocator to work. void -main() +main(int hartid) { + w_tp(hartid); // save hartid where cpuid() can find it uartinit(); // serial port consoleinit(); - printf("entering main()\n"); + printf("entering main() on hart %d\n", hartid); kinit(); // physical page allocator kvminit(); // kernel page table procinit(); // process table diff --git a/memide.c b/memide.c deleted file mode 100644 index ba267ac..0000000 --- a/memide.c +++ /dev/null @@ -1,60 +0,0 @@ -// Fake IDE disk; stores blocks in memory. -// Useful for running kernel without scratch disk. - -#include "types.h" -#include "defs.h" -#include "param.h" -#include "mmu.h" -#include "proc.h" -#include "x86.h" -#include "traps.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "fs.h" -#include "buf.h" - -extern uchar _binary_fs_img_start[], _binary_fs_img_size[]; - -static int disksize; -static uchar *memdisk; - -void -ideinit(void) -{ - memdisk = _binary_fs_img_start; - disksize = (uint)_binary_fs_img_size/BSIZE; -} - -// Interrupt handler. -void -ideintr(void) -{ - // no-op -} - -// Sync buf with disk. -// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. -// Else if B_VALID is not set, read buf from disk, set B_VALID. -void -iderw(struct buf *b) -{ - uchar *p; - - if(!holdingsleep(&b->lock)) - panic("iderw: buf not locked"); - if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) - panic("iderw: nothing to do"); - if(b->dev != 1) - panic("iderw: request not for disk 1"); - if(b->blockno >= disksize) - panic("iderw: block out of range"); - - p = memdisk + b->blockno*BSIZE; - - if(b->flags & B_DIRTY){ - b->flags &= ~B_DIRTY; - memmove(p, b->data, BSIZE); - } else - memmove(b->data, p, BSIZE); - b->flags |= B_VALID; -} diff --git a/memlayout.h b/memlayout.h index db7c076..9c9cfdb 100644 --- a/memlayout.h +++ b/memlayout.h @@ -23,8 +23,7 @@ // local interrupt controller, which contains the timer. #define CLINT 0x2000000L -#define CLINT_MSIP0 (CLINT + 0x0) -#define CLINT_MTIMECMP0 (CLINT + 0x4000) +#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid)) #define CLINT_MTIME (CLINT + 0xBFF8) // qemu puts programmable interrupt controller here. diff --git a/mp.c b/mp.c deleted file mode 100644 index e36e45c..0000000 --- a/mp.c +++ /dev/null @@ -1,139 +0,0 @@ -// Multiprocessor support -// Search memory for MP description structures. -// http://developer.intel.com/design/pentium/datashts/24201606.pdf - -#include "types.h" -#include "defs.h" -#include "param.h" -#include "memlayout.h" -#include "mp.h" -#include "x86.h" -#include "mmu.h" -#include "proc.h" - -struct cpu cpus[NCPU]; -int ncpu; -uchar ioapicid; - -static uchar -sum(uchar *addr, int len) -{ - int i, sum; - - sum = 0; - for(i=0; iphysaddr == 0) - return 0; - conf = (struct mpconf*) P2V((uint64) mp->physaddr); - if(memcmp(conf, "PCMP", 4) != 0) - return 0; - if(conf->version != 1 && conf->version != 4) - return 0; - if(sum((uchar*)conf, conf->length) != 0) - return 0; - *pmp = mp; - return conf; -} - -void -mpinit(void) -{ - uchar *p, *e; - int ismp; - struct mp *mp; - struct mpconf *conf; - struct mpproc *proc; - struct mpioapic *ioapic; - - if((conf = mpconfig(&mp)) == 0) - panic("Expect to run on an SMP"); - ismp = 1; - lapic = P2V((uint64)conf->lapicaddr_p); - for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; papicid; // apicid may differ from ncpu - ncpu++; - } - p += sizeof(struct mpproc); - continue; - case MPIOAPIC: - ioapic = (struct mpioapic*)p; - ioapicid = ioapic->apicno; - p += sizeof(struct mpioapic); - continue; - case MPBUS: - case MPIOINTR: - case MPLINTR: - p += 8; - continue; - default: - ismp = 0; - break; - } - } - if(!ismp) - panic("Didn't find a suitable machine"); - - if(mp->imcrp){ - // Bochs doesn't support IMCR, so this doesn't run on Bochs. - // But it would on real hardware. - outb(0x22, 0x70); // Select IMCR - outb(0x23, inb(0x23) | 1); // Mask external interrupts. - } -} diff --git a/mp.h b/mp.h deleted file mode 100644 index 5964b63..0000000 --- a/mp.h +++ /dev/null @@ -1,56 +0,0 @@ -// See MultiProcessor Specification Version 1.[14] - -struct mp { // floating pointer - uchar signature[4]; // "_MP_" - uint32 physaddr; // phys addr of MP config table - uchar length; // 1 - uchar specrev; // [14] - uchar checksum; // all bytes must add up to 0 - uchar type; // MP system config type - uchar imcrp; - uchar reserved[3]; -}; - -struct mpconf { // configuration table header - uchar signature[4]; // "PCMP" - ushort length; // total table length - uchar version; // [14] - uchar checksum; // all bytes must add up to 0 - uchar product[20]; // product id - uint32 oemtable; // OEM table pointer - ushort oemlength; // OEM table length - ushort entry; // entry count - uint32 lapicaddr_p; // address of local APIC - ushort xlength; // extended table length - uchar xchecksum; // extended table checksum - uchar reserved; -}; - -struct mpproc { // processor table entry - uchar type; // entry type (0) - uchar apicid; // local APIC id - uchar version; // local APIC verison - uchar flags; // CPU flags - #define MPBOOT 0x02 // This proc is the bootstrap processor. - uchar signature[4]; // CPU signature - uint feature; // feature flags from CPUID instruction - uchar reserved[8]; -}; - -struct mpioapic { // I/O APIC table entry - uchar type; // entry type (2) - uchar apicno; // I/O APIC id - uchar version; // I/O APIC version - uchar flags; // I/O APIC flags - uint32 addr_p; // I/O APIC address -}; - -// Table entry types -#define MPPROC 0x00 // One per processor -#define MPBUS 0x01 // One per bus -#define MPIOAPIC 0x02 // One per I/O APIC -#define MPIOINTR 0x03 // One per bus interrupt source -#define MPLINTR 0x04 // One per system interrupt source - -//PAGEBREAK! -// Blank page. diff --git a/picirq.c b/picirq.c deleted file mode 100644 index e26957f..0000000 --- a/picirq.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "types.h" -#include "x86.h" -#include "traps.h" - -// I/O Addresses of the two programmable interrupt controllers -#define IO_PIC1 0x20 // Master (IRQs 0-7) -#define IO_PIC2 0xA0 // Slave (IRQs 8-15) - -// Don't use the 8259A interrupt controllers. Xv6 assumes SMP hardware. -void -picinit(void) -{ - // mask all interrupts - outb(IO_PIC1+1, 0xFF); - outb(IO_PIC2+1, 0xFF); -} - -//PAGEBREAK! -// Blank page. diff --git a/proc.c b/proc.c index 768aa6d..766fd93 100644 --- a/proc.c +++ b/proc.c @@ -32,28 +32,33 @@ procinit(void) initlock(&ptable.lock, "ptable"); } -// Must be called with interrupts disabled. -// XXX riscv +// Must be called with interrupts disabled, +// to prevent race with process being moved +// to a different CPU. int -cpuid() { - return 0; +cpuid() +{ + int id = r_tp(); + return id; } // Return this core's cpu struct. -// XXX riscv +// Interrupts must be disabled. struct cpu* mycpu(void) { - struct cpu *c; - c = &cpus[0]; + int id = cpuid(); + struct cpu *c = &cpus[id]; return c; } -// Disable interrupts so that we are not rescheduled -// while reading proc from the cpu structure -// XXX riscv +// Return the current struct proc *. struct proc* myproc(void) { - return cpus[0].proc; + // XXX push intr off + struct cpu *c = mycpu(); + struct proc *p = c->proc; + // XXX pop intr + return p; } //PAGEBREAK: 32 diff --git a/proc.h b/proc.h index 7e1149f..00a1cb9 100644 --- a/proc.h +++ b/proc.h @@ -20,10 +20,7 @@ struct context { // Per-CPU state struct cpu { - uint64 syscallno; // Temporary used by sysentry - uint64 usp; // Temporary used by sysentry struct proc *proc; // The process running on this cpu or null - struct cpu *cpu; // XXX struct context scheduler; // swtch() here to enter scheduler volatile uint started; // Has the CPU started? int ncli; // Depth of pushcli nesting. @@ -31,7 +28,6 @@ struct cpu { }; extern struct cpu cpus[NCPU]; -extern int ncpu; //PAGEBREAK: 17 @@ -47,9 +43,9 @@ extern int ncpu; struct trapframe { /* 0 */ uint64 kernel_satp; /* 8 */ uint64 kernel_sp; - /* 16 */ uint64 kernel_trap; // address of trap() + /* 16 */ uint64 kernel_trap; // usertrap() /* 24 */ uint64 epc; // saved user program counter - /* 32 */ uint64 unused; + /* 32 */ uint64 hartid; /* 40 */ uint64 ra; /* 48 */ uint64 sp; /* 56 */ uint64 gp; diff --git a/riscv.h b/riscv.h index 14c8738..b12e5d3 100644 --- a/riscv.h +++ b/riscv.h @@ -1,3 +1,12 @@ +// which hart (core) is this? +static inline uint64 +r_mhartid() +{ + uint64 x; + asm("csrr %0, mhartid" : "=r" (x) ); + return x; +} + // Machine Status Register, mstatus #define MSTATUS_MPP_MASK (3L << 11) @@ -279,6 +288,22 @@ r_sp() return x; } +// read and write tp, the thread pointer, which holds +// this core's hartid (core number), the index into cpus[]. +static inline uint64 +r_tp() +{ + uint64 x; + asm("mv %0, tp" : "=r" (x) ); + return x; +} + +static inline void +w_tp(uint64 x) +{ + asm("mv tp, %0" : : "r" (x)); +} + #define PGSIZE 4096 // bytes per page #define PGSHIFT 12 // bits of offset within a page diff --git a/start.c b/start.c index 8743955..53edb8e 100644 --- a/start.c +++ b/start.c @@ -1,25 +1,19 @@ #include "types.h" +#include "param.h" #include "memlayout.h" #include "riscv.h" #include "defs.h" void main(); -// entry.S uses this as the initial stack. -__attribute__ ((aligned (16))) char stack0[4096]; +// entry.S needs one stack per CPU. +__attribute__ ((aligned (16))) char stack0[4096 * NCPU]; // assembly code in kernelvec for machine-mode timer interrupt. extern void machinevec(); -// scratch area for timer interrupt. -uint64 mscratch0[8]; - -__attribute__ ((aligned (16))) -void -xyzzy() -{ - uartputc('I'); -} +// scratch area for timer interrupt, one per CPU. +uint64 mscratch0[NCPU * 32]; // entry.S jumps here in machine mode on stack0. void @@ -42,15 +36,19 @@ mstart() w_medeleg(0xffff); w_mideleg(0xffff); - // set up to receive timer interrupts in machine mode. - *(uint64*)CLINT_MTIMECMP0 = *(uint64*)CLINT_MTIME + 10000; - mscratch0[4] = CLINT_MTIMECMP0; - mscratch0[5] = 10000000; - w_mscratch((uint64)mscratch0); + // set up to receive timer interrupts in machine mode, + // for pre-emptive switching and (on hart 0) to drive time. + int id = r_mhartid(); + uint64 *scratch = &mscratch0[32 * id]; + *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + 10000; + scratch[4] = CLINT_MTIMECMP(id); + scratch[5] = 10000000; + w_mscratch((uint64)scratch); w_mtvec((uint64)machinevec); w_mstatus(r_mstatus() | MSTATUS_MIE); w_mie(r_mie() | MIE_MTIE); - // jump to main in supervisor mode. - asm("mret"); + // call main(hartid) in supervisor mode. + asm("csrr a0, mhartid ; \ + mret"); } diff --git a/trampoline.S b/trampoline.S index 5886942..dd4eb02 100644 --- a/trampoline.S +++ b/trampoline.S @@ -120,6 +120,9 @@ trampin: # restore kernel stack pointer from p->tf->kernel_sp ld sp, 8(a0) + # make tp hold the current hartid, from p->tf->hartid + ld tp, 32(a0) + # remember the address of usertrap(), p->tf->kernel_trap ld t0, 16(a0) diff --git a/trap.c b/trap.c index 5f5d4a0..693c596 100644 --- a/trap.c +++ b/trap.c @@ -24,8 +24,6 @@ trapinit(void) // set up to take exceptions and traps while in the kernel. w_stvec((uint64)kernelvec); - // time, cycle, instret CSRs - initlock(&tickslock, "time"); } @@ -45,10 +43,6 @@ usertrap(void) // since we're now in the kernel. w_stvec((uint64)kernelvec); - //printf("mtimecmp %x mtime %x\n", *(uint64*)CLINT_MTIMECMP0, *(uint64*)CLINT_MTIME); - - *(uint64*)CLINT_MTIMECMP0 = *(uint64*)CLINT_MTIME + 10000; - struct proc *p = myproc(); // save user program counter. @@ -102,6 +96,7 @@ usertrapret(void) p->tf->kernel_satp = r_satp(); p->tf->kernel_sp = (uint64)p->kstack + PGSIZE; p->tf->kernel_trap = (uint64)usertrap; + p->tf->hartid = r_tp(); // set up the registers that trampoline.S's sret will use // to get to user space. @@ -132,9 +127,12 @@ kerneltrap() { uint64 sstatus = r_sstatus(); uint64 scause = r_scause(); + uint64 sepc = r_sepc(); // XXX needed only for check at end? if((sstatus & SSTATUS_SPP) == 0) panic("kerneltrap: not from supervisor mode"); + if(intr_get() != 0) + panic("kerneltrap: interrupts enabled"); if(devintr() == 0){ printf("scause 0x%p\n", scause); @@ -142,12 +140,11 @@ kerneltrap() panic("kerneltrap"); } - // turn off interrupts to ensure we - // return with the correct sstatus. - intr_off(); - - // restore previous interrupt status. - w_sstatus(sstatus); + // XXX assert that we don't have to save/restore sstatus or sepc. + if(r_sstatus() != sstatus) + panic("kerneltrap sstatus"); + if(r_sepc() != sepc) + panic("kerneltrap sepc"); } // check if it's an external interrupt or software interrupt,