diff options
| author | Robert Morris <rtm@csail.mit.edu> | 2019-06-05 11:42:03 -0400 | 
|---|---|---|
| committer | Robert Morris <rtm@csail.mit.edu> | 2019-06-05 11:42:03 -0400 | 
| commit | f1a727b971a59bab6025b4c4111342c27356ca40 (patch) | |
| tree | d22d52c613bfc003e6fb75b5d137aeff9d954201 | |
| parent | ec3d3a1fceee437c640f9c5c05fc517edfb1899e (diff) | |
| download | xv6-labs-f1a727b971a59bab6025b4c4111342c27356ca40.tar.gz xv6-labs-f1a727b971a59bab6025b4c4111342c27356ca40.tar.bz2 xv6-labs-f1a727b971a59bab6025b4c4111342c27356ca40.zip | |
start at support for multiple CPUs
| -rw-r--r-- | .gdbinit.tmpl-i386 | 1 | ||||
| -rw-r--r-- | .gdbinit.tmpl-riscv | 1 | ||||
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | asm.h | 18 | ||||
| -rw-r--r-- | bootmain.c | 96 | ||||
| -rw-r--r-- | entry.S | 13 | ||||
| -rw-r--r-- | kbd.c | 50 | ||||
| -rw-r--r-- | kbd.h | 112 | ||||
| -rw-r--r-- | lapic.c | 229 | ||||
| -rw-r--r-- | main.c | 5 | ||||
| -rw-r--r-- | memide.c | 60 | ||||
| -rw-r--r-- | memlayout.h | 3 | ||||
| -rw-r--r-- | mp.c | 139 | ||||
| -rw-r--r-- | mp.h | 56 | ||||
| -rw-r--r-- | picirq.c | 19 | ||||
| -rw-r--r-- | proc.c | 27 | ||||
| -rw-r--r-- | proc.h | 8 | ||||
| -rw-r--r-- | riscv.h | 25 | ||||
| -rw-r--r-- | start.c | 34 | ||||
| -rw-r--r-- | trampoline.S | 3 | ||||
| -rw-r--r-- | trap.c | 21 | 
21 files changed, 87 insertions, 837 deletions
| 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 @@ -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) @@ -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); -} @@ -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: @@ -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); -} @@ -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; -} @@ -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. @@ -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; i<len; i++) -    sum += addr[i]; -  return sum; -} - -// Look for an MP structure in the len bytes at addr. -static struct mp* -mpsearch1(uint64 a, int len) -{ -  uchar *e, *p, *addr; - -  addr = P2V(a); -  e = addr+len; -  for(p = addr; p < e; p += sizeof(struct mp)) -    if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0) -      return (struct mp*)p; -  return 0; -} - -// Search for the MP Floating Pointer Structure, which according to the -// spec is in one of the following three locations: -// 1) in the first KB of the EBDA; -// 2) in the last KB of system base memory; -// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF. -static struct mp* -mpsearch(void) -{ -  uchar *bda; -  uint p; -  struct mp *mp; - -  bda = (uchar *) P2V(0x400); -  if((p = ((bda[0x0F]<<8)| bda[0x0E]) << 4)){ -    if((mp = mpsearch1(p, 1024))) -      return mp; -  } else { -    p = ((bda[0x14]<<8)|bda[0x13])*1024; -    if((mp = mpsearch1(p-1024, 1024))) -      return mp; -  } -  return mpsearch1(0xF0000, 0x10000); -} - -// Search for an MP configuration table.  For now, -// don't accept the default configurations (physaddr == 0). -// Check for correct signature, calculate the checksum and, -// if correct, check the version. -// To do: check extended table checksum. -static struct mpconf* -mpconfig(struct mp **pmp) -{ -  struct mpconf *conf; -  struct mp *mp; - -  if((mp = mpsearch()) == 0 || mp->physaddr == 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; p<e; ){ -    switch(*p){ -    case MPPROC: -      proc = (struct mpproc*)p; -      if(ncpu < NCPU) { -        cpus[ncpu].apicid = proc->apicid;  // 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. -  } -} @@ -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. @@ -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 @@ -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; @@ -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 @@ -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) @@ -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, | 
