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); + } } |