summaryrefslogtreecommitdiff
path: root/mp.c
diff options
context:
space:
mode:
authorkaashoek <kaashoek>2006-06-22 01:28:57 +0000
committerkaashoek <kaashoek>2006-06-22 01:28:57 +0000
commit21a88fd487177841c882d9017bd9f4476801c6f6 (patch)
treebfa061e00662efde2186d6c0498fc78f889356ce /mp.c
parent7baa34a421e4c970ee90c2537ceacd7230f2474e (diff)
downloadxv6-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.c284
1 files changed, 254 insertions, 30 deletions
diff --git a/mp.c b/mp.c
index 9d47e50..d3db697 100644
--- a/mp.c
+++ b/mp.c
@@ -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);
+ }
}