diff options
| author | kaashoek <kaashoek> | 2006-06-22 01:28:57 +0000 | 
|---|---|---|
| committer | kaashoek <kaashoek> | 2006-06-22 01:28:57 +0000 | 
| commit | 21a88fd487177841c882d9017bd9f4476801c6f6 (patch) | |
| tree | bfa061e00662efde2186d6c0498fc78f889356ce /mp.c | |
| parent | 7baa34a421e4c970ee90c2537ceacd7230f2474e (diff) | |
| download | xv6-labs-21a88fd487177841c882d9017bd9f4476801c6f6.tar.gz xv6-labs-21a88fd487177841c882d9017bd9f4476801c6f6.tar.bz2 xv6-labs-21a88fd487177841c882d9017bd9f4476801c6f6.zip | |
checkpoint. booting second processor.  stack is messed up, but thanks to cliff
and plan 9 code, at least boots and gets into C code.
Diffstat (limited to 'mp.c')
| -rw-r--r-- | mp.c | 284 | 
1 files changed, 254 insertions, 30 deletions
| @@ -2,11 +2,201 @@  #include "mp.h"  #include "defs.h"  #include "memlayout.h" +#include "param.h" +#include "x86.h" +#include "mmu.h" -static struct _MP_* _mp_;  /* The MP floating point structure */ +/*  + * Credit: Plan 9 sources, Intel MP spec, and Cliff Frey + */ + +enum {					/* Local APIC registers */ +  LAPIC_ID  = 0x0020,	/* ID */ +  LAPIC_VER = 0x0030,	/* Version */ +  LAPIC_TPR = 0x0080,	/* Task Priority */ +  LAPIC_APR = 0x0090,	/* Arbitration Priority */ +  LAPIC_PPR = 0x00A0,	/* Processor Priority */ +  LAPIC_EOI = 0x00B0,	/* EOI */ +  LAPIC_LDR = 0x00D0,	/* Logical Destination */ +  LAPIC_DFR = 0x00E0,	/* Destination Format */ +  LAPIC_SVR = 0x00F0,	/* Spurious Interrupt Vector */ +  LAPIC_ISR = 0x0100,	/* Interrupt Status (8 registers) */ +  LAPIC_TMR = 0x0180,	/* Trigger Mode (8 registers) */ +  LAPIC_IRR = 0x0200,	/* Interrupt Request (8 registers) */ +  LAPIC_ESR = 0x0280,	/* Error Status */ +  LAPIC_ICRLO = 0x0300,	/* Interrupt Command */ +  LAPIC_ICRHI = 0x0310,	/* Interrupt Command [63:32] */ +  LAPIC_TIMER = 0x0320,	/* Local Vector Table 0 (TIMER) */ +  LAPIC_PCINT = 0x0340,	/* Performance Counter LVT */ +  LAPIC_LINT0 = 0x0350,	/* Local Vector Table 1 (LINT0) */ +  LAPIC_LINT1 = 0x0360,	/* Local Vector Table 2 (LINT1) */ +  LAPIC_ERROR = 0x0370,	/* Local Vector Table 3 (ERROR) */ +  LAPIC_TICR = 0x0380,	/* Timer Initial Count */ +  LAPIC_TCCR = 0x0390,	/* Timer Current Count */ +  LAPIC_TDCR = 0x03E0,	/* Timer Divide Configuration */ +}; + +enum {					/* LAPIC_SVR */ +  LAPIC_ENABLE	= 0x00000100,	/* Unit Enable */ +  LAPIC_FOCUS	= 0x00000200,	/* Focus Processor Checking Disable */ +}; + +enum {					/* LAPIC_ICRLO */ +					/* [14] IPI Trigger Mode Level (RW) */ +  LAPIC_DEASSERT = 0x00000000,	/* Deassert level-sensitive interrupt */ +  LAPIC_ASSERT	= 0x00004000,	/* Assert level-sensitive interrupt */ + +  /* [17:16] Remote Read Status */ +  LAPIC_INVALID	= 0x00000000,	/* Invalid */ +  LAPIC_WAIT	= 0x00010000,	/* In-Progress */ +  LAPIC_VALID	= 0x00020000,	/* Valid */ + +  /* [19:18] Destination Shorthand */ +  LAPIC_FIELD	= 0x00000000,	/* No shorthand */ +  LAPIC_SELF	= 0x00040000,	/* Self is single destination */ +  LAPIC_ALLINC	= 0x00080000,	/* All including self */ +  LAPIC_ALLEXC	= 0x000C0000,	/* All Excluding self */ +}; + +enum {					/* LAPIC_ESR */ +  LAPIC_SENDCS	= 0x00000001,	/* Send CS Error */ +  LAPIC_RCVCS	= 0x00000002,	/* Receive CS Error */ +  LAPIC_SENDACCEPT = 0x00000004,	/* Send Accept Error */ +  LAPIC_RCVACCEPT = 0x00000008,	/* Receive Accept Error */ +  LAPIC_SENDVECTOR = 0x00000020,	/* Send Illegal Vector */ +  LAPIC_RCVVECTOR = 0x00000040,	/* Receive Illegal Vector */ +  LAPIC_REGISTER = 0x00000080,	/* Illegal Register Address */ +}; + +enum {					/* LAPIC_TIMER */ +					/* [17] Timer Mode (RW) */ +  LAPIC_ONESHOT	= 0x00000000,	/* One-shot */ +  LAPIC_PERIODIC = 0x00020000,	/* Periodic */ + +  /* [19:18] Timer Base (RW) */ +  LAPIC_CLKIN	= 0x00000000,	/* use CLKIN as input */ +  LAPIC_TMBASE	= 0x00040000,	/* use TMBASE */ +  LAPIC_DIVIDER	= 0x00080000,	/* use output of the divider */ +}; + +enum {					/* LAPIC_TDCR */ +  LAPIC_X2 = 0x00000000,	/* divide by 2 */ +  LAPIC_X4 = 0x00000001,	/* divide by 4 */ +  LAPIC_X8 = 0x00000002,	/* divide by 8 */ +  LAPIC_X16 = 0x00000003,	/* divide by 16 */ +  LAPIC_X32 = 0x00000008,	/* divide by 32 */ +  LAPIC_X64 = 0x00000009,	/* divide by 64 */ +  LAPIC_X128 = 0x0000000A,	/* divide by 128 */ +  LAPIC_X1 = 0x0000000B,	/* divide by 1 */ +}; + +#define APBOOTCODE 0x7000 // XXX hack + +static struct MP* mp;  /* The MP floating point structure */ +static uint32_t *lapicaddr; +static struct cpu { +  uint8_t apicid;       /* Local APIC ID */ +  int lintr[2];		/* Local APIC */ +} cpu[NCPU];  static int ncpu; +static struct cpu *bcpu; + +static int +lapic_read(int r) +{ +  return *(lapicaddr+(r/sizeof(*lapicaddr))); +} + +static void +lapic_write(int r, int data) +{ +  *(lapicaddr+(r/sizeof(*lapicaddr))) = data; +} + +static void +lapic_init(int c) +{ +  uint32_t r, lvt; + +  cprintf("lapic_init %d\n", c); +  lapic_write(LAPIC_DFR, 0xFFFFFFFF); +  r = (lapic_read(LAPIC_ID)>>24) & 0xFF; +  lapic_write(LAPIC_LDR, (1<<r)<<24); +  lapic_write(LAPIC_TPR, 0xFF); +  lapic_write(LAPIC_SVR, LAPIC_ENABLE|(IRQ_OFFSET+IRQ_SPURIOUS)); + +  /* +   * Set the local interrupts. It's likely these should just be +   * masked off for SMP mode as some Pentium Pros have problems if +   * LINT[01] are set to ExtINT. +   * Acknowledge any outstanding interrupts. +   */ +  lapic_write(LAPIC_LINT0, cpu[c].lintr[0]); +  lapic_write(LAPIC_LINT1, cpu[c].lintr[1]); +  lapic_write(LAPIC_EOI, 0); + +  lvt = (lapic_read(LAPIC_VER)>>16) & 0xFF; +  if(lvt >= 4) +    lapic_write(LAPIC_PCINT, APIC_IMASK); +  lapic_write(LAPIC_ERROR, IRQ_OFFSET+IRQ_ERROR); +  lapic_write(LAPIC_ESR, 0); +  lapic_read(LAPIC_ESR); + +  /* +   * Issue an INIT Level De-Assert to synchronise arbitration ID's. +   */ +  lapic_write(LAPIC_ICRHI, 0); +  lapic_write(LAPIC_ICRLO, LAPIC_ALLINC|APIC_LEVEL|LAPIC_DEASSERT|APIC_INIT); +  while(lapic_read(LAPIC_ICRLO) & APIC_DELIVS) +    ; -static struct _MP_* +  /* +   * Do not allow acceptance of interrupts until all initialisation +   * for this processor is done. For the bootstrap processor this can be +   * early duing initialisation. For the application processors this should +   * be after the bootstrap processor has lowered priority and is accepting +   * interrupts. +   */ +  lapic_write(LAPIC_TPR, 0); +  cprintf("Done init of an apic\n"); +} + +static void +lapic_online(void)  +{ +  lapic_write(LAPIC_TPR, 0); +} + +int +lapic_cpu_number(void) +{ +  return (lapic_read(LAPIC_ID)>>24) & 0xFF; +} + +static void +lapic_startap(struct cpu *c, int v) +{ +  int crhi, i; +  volatile int j = 0; + +  crhi = c->apicid<<24; +  lapic_write(LAPIC_ICRHI, crhi); +  lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_LEVEL|LAPIC_ASSERT|APIC_INIT); + +  while (j++ < 10000) {;} +  lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_LEVEL|LAPIC_DEASSERT|APIC_INIT); + +  while (j++ < 1000000) {;} + +  // in p9 code, this was i < 2, which is what the spec says on page B-3 +  for(i = 0; i < 1; i++){ +    lapic_write(LAPIC_ICRHI, crhi); +    lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_EDGE|APIC_STARTUP|(v/PGSIZE)); +    while (j++ < 100000) {;} +  } +} + +static struct MP*  mp_scan(uint8_t *addr, int len)  {    uint8_t *e, *p, sum; @@ -14,24 +204,24 @@ mp_scan(uint8_t *addr, int len)    cprintf("scanning: 0x%x\n", (uint32_t)addr);    e = addr+len; -  for(p = addr; p < e; p += sizeof(struct _MP_)){ +  for(p = addr; p < e; p += sizeof(struct MP)){      if(memcmp(p, "_MP_", 4))        continue;      sum = 0; -    for(i = 0; i < sizeof(struct _MP_); i++) +    for(i = 0; i < sizeof(struct MP); i++)        sum += p[i];      if(sum == 0) -      return (struct _MP_ *)p; +      return (struct MP *)p;    }    return 0;  } -static struct _MP_* +static struct MP*  mp_search(void)  {    uint8_t *bda;    uint32_t p; -  struct _MP_ *mp; +  struct MP *mp;    /*     * Search for the MP Floating Pointer Structure, which according to the @@ -56,7 +246,7 @@ mp_search(void)  static int   mp_detect(void)  { -  struct PCMP *pcmp; +  struct MPCTB *pcmp;    uint8_t *p, sum;    uint32_t length; @@ -67,10 +257,10 @@ mp_detect(void)     * if correct, check the version.     * To do: check extended table checksum.     */ -  if((_mp_ = mp_search()) == 0 || _mp_->physaddr == 0) +  if((mp = mp_search()) == 0 || mp->physaddr == 0)      return 1; -  pcmp = KADDR(_mp_->physaddr); +  pcmp = KADDR(mp->physaddr);    if(memcmp(pcmp, "PCMP", 4))      return 2; @@ -82,48 +272,65 @@ mp_detect(void)    if(sum || (pcmp->version != 1 && pcmp->version != 4))      return 3; -  cprintf("MP spec rev #: %x\n", _mp_->specrev); +  cprintf("MP spec rev #: %x\n", mp->specrev);    return 0;  } +int +mp_isbcpu() +{ +  if (bcpu == 0) return 1; +  else return 0; +} +  void -mpinit() +mp_init()  {     int r;    uint8_t *p, *e; -  struct PCMP *pcmp; +  struct MPCTB *mpctb; +  struct MPPE *proc; +  struct cpu *c;    ncpu = 0;    if ((r = mp_detect()) != 0) return; -  cprintf ("This computer is multiprocessor!\n"); + +  cprintf ("This computer is a multiprocessor!\n");    /*     * Run through the table saving information needed for starting     * application processors and initialising any I/O APICs. The table     * is guaranteed to be in order such that only one pass is necessary.     */ -  pcmp = KADDR(_mp_->physaddr); -  p = ((uint8_t*)pcmp)+sizeof(struct PCMP); -  e = ((uint8_t*)pcmp)+pcmp->length; +  mpctb = KADDR(mp->physaddr); +  lapicaddr = KADDR(mpctb->lapicaddr); +  cprintf("apicaddr: %x\n", lapicaddr); +  p = ((uint8_t*)mpctb)+sizeof(struct MPCTB); +  e = ((uint8_t*)mpctb)+mpctb->length;    while(p < e) {      switch(*p){ -    case PcmpPROCESSOR: -      cprintf("a processor\n"); +    case MPPROCESSOR: +      proc = (struct MPPE *) p; +      cpu[ncpu].apicid = proc->apicid; +      cpu[ncpu].lintr[0] = APIC_IMASK; +      cpu[ncpu].lintr[1] = APIC_IMASK; +      cprintf("a processor %x\n", cpu[ncpu].apicid); +      if (proc->flags & MPBP) { +	bcpu = &cpu[ncpu]; +      }        ncpu++; -      p += sizeof(struct PCMPprocessor); +      p += sizeof(struct MPPE);        continue; -    case PcmpBUS: -      cprintf("a bus\n"); -      p += sizeof(struct PCMPbus); +    case MPBUS: +      p += sizeof(struct MPBE);        continue; -    case PcmpIOAPIC: -      cprintf("an IO APIC\n"); -      p += sizeof(struct PCMPioapic); +    case MPIOAPIC: +      cprintf("an I/O APIC\n"); +      p += sizeof(struct MPIOAPIC);        continue; -    case PcmpIOINTR: -      cprintf("an IO interrupt assignment\n"); -      p += sizeof(struct PCMPintr); +    case MPIOINTR: +      p += sizeof(struct MPIE);        continue;      default:        cprintf("mpinit: unknown PCMP type 0x%x (e-p 0x%x)\n", *p, e-p); @@ -134,6 +341,23 @@ mpinit()        break;      }    } +   +  lapic_init(cpu-bcpu); +  cprintf("ncpu: %d boot %d\n", ncpu, cpu-bcpu); -  cprintf("ncpu: %d\n", ncpu); +  lapic_online(); + +  extern uint8_t _binary_bootother_start[], _binary_bootother_size[]; +  memmove(KADDR(APBOOTCODE),_binary_bootother_start,  +	  (uint32_t) _binary_bootother_size); + +  acquire_spinlock(&kernel_lock); +  for (c = cpu; c < &cpu[ncpu]; c++) { +    if (c == bcpu) continue; +    cprintf ("starting processor %d\n", c - cpu); +    release_grant_spinlock(&kernel_lock, c - cpu); +    lapic_startap(c, (uint32_t) KADDR(APBOOTCODE)); +    acquire_spinlock(&kernel_lock); +    cprintf ("done starting processor %d\n", c - cpu); +  }  } | 
