Simplify MP hardware code.

Mainly delete unused constants and code.

Move mp_startthem to main.c as bootothers.
This commit is contained in:
rsc 2007-08-27 22:53:31 +00:00
parent b63bb0fd00
commit 99b11b6c64
6 changed files with 213 additions and 453 deletions

View file

@ -1,85 +1,82 @@
// The I/O APIC manages hardware interrupts for an SMP system.
// http://www.intel.com/design/chipsets/datashts/29056601.pdf
#include "types.h" #include "types.h"
#include "mp.h" #include "mp.h"
#include "defs.h" #include "defs.h"
#include "x86.h" #include "x86.h"
#include "traps.h" #include "traps.h"
#include "ioapic.h"
#define IOAPIC 0xFEC00000 // Default physical address of IO APIC
#define REG_ID 0x00 // Register index: ID
#define REG_VER 0x01 // Register index: version
#define REG_TABLE 0x10 // Redirection table base
// The redirection table starts at REG_TABLE and uses
// two registers to configure each interrupt.
// The first (low) register in a pair contains configuration bits.
// The second (high) register contains a bitmask telling which
// CPUs can serve that interrupt.
#define INT_DISABLED 0x00100000 // Interrupt disabled
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
volatile struct ioapic *ioapic;
// IO APIC MMIO structure: write reg, then read or write data.
struct ioapic { struct ioapic {
uint ioregsel; uint p01; uint p02; uint p03; uint reg;
uint iowin; uint p11; uint p12; uint p13; uint pad[3];
uint data;
}; };
#define IOAPIC_REDTBL_LO(i) (IOAPIC_REDTBL + (i) * 2)
#define IOAPIC_REDTBL_HI(i) (IOAPIC_REDTBL_LO(i) + 1)
static uint static uint
ioapic_read(struct ioapic *io, int reg) ioapic_read(int reg)
{ {
io->ioregsel = reg; ioapic->reg = reg;
return io->iowin; return ioapic->data;
} }
static void static void
ioapic_write(struct ioapic *io, int reg, uint val) ioapic_write(int reg, uint data)
{ {
io->ioregsel = reg; ioapic->reg = reg;
io->iowin = val; ioapic->data = data;
} }
void void
ioapic_init(void) ioapic_init(void)
{ {
struct ioapic *io; int i, id, maxintr;
uint l, h;
int nintr;
uchar id;
int i;
if(!ismp) if(!ismp)
return; return;
io = (struct ioapic*) IO_APIC_BASE; ioapic = (volatile struct ioapic*)IOAPIC;
l = ioapic_read(io, IOAPIC_VER); maxintr = (ioapic_read(REG_VER) >> 16) & 0xFF;
nintr = ((l & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1; id = ioapic_read(REG_ID) >> 24;
id = ioapic_read(io, IOAPIC_ID) >> APIC_ID_SHIFT;
if(id != ioapic_id) if(id != ioapic_id)
cprintf("ioapic_init: id isn't equal to ioapic_id; not a MP\n"); cprintf("ioapic_init: id isn't equal to ioapic_id; not a MP\n");
for(i = 0; i < nintr; i++) {
// active-hi and edge-triggered for ISA interrupts // Mark all interrupts edge-triggered, active high, disabled,
// Assume that pin 0 on the first I/O APIC is an ExtINT pin. // and not routed to any CPUs.
// Assume that pins 1-15 are ISA interrupts for(i = 0; i <= maxintr; i++){
l = ioapic_read(io, IOAPIC_REDTBL_LO(i)); ioapic_write(REG_TABLE+2*i, INT_DISABLED | (IRQ_OFFSET + i));
l = l & ~IOART_INTMASK; // allow INTs ioapic_write(REG_TABLE+2*i+1, 0);
l |= IOART_INTMSET;
l = l & ~IOART_INTPOL; // active hi
l = l & ~IOART_TRGRMOD; // edgee triggered
l = l & ~IOART_DELMOD; // fixed
l = l & ~IOART_DESTMOD; // physical mode
l = l | (IRQ_OFFSET + i); // vector
ioapic_write(io, IOAPIC_REDTBL_LO(i), l);
h = ioapic_read(io, IOAPIC_REDTBL_HI(i));
h &= ~IOART_DEST;
ioapic_write(io, IOAPIC_REDTBL_HI(i), h);
} }
} }
void void
ioapic_enable(int irq, int cpunum) ioapic_enable(int irq, int cpunum)
{ {
uint l, h;
struct ioapic *io;
if(!ismp) if(!ismp)
return; return;
io = (struct ioapic*) IO_APIC_BASE; // Mark interrupt edge-triggered, active high,
l = ioapic_read(io, IOAPIC_REDTBL_LO(irq)); // enabled, and routed to the given cpunum,
l = l & ~IOART_INTMASK; // allow INTs // which happens to be that cpu's APIC ID.
ioapic_write(io, IOAPIC_REDTBL_LO(irq), l); ioapic_write(REG_TABLE+2*irq, IRQ_OFFSET + irq);
h = ioapic_read(io, IOAPIC_REDTBL_HI(irq)); ioapic_write(REG_TABLE+2*irq+1, cpunum << 24);
h &= ~IOART_DEST;
h |= (cpunum << APIC_ID_SHIFT);
ioapic_write(io, IOAPIC_REDTBL_HI(irq), h);
} }

View file

@ -1,88 +0,0 @@
#define IO_APIC_BASE 0xFEC00000 // Default phys addr of IO APIC
#define IOAPIC_WINDOW 0x10 // Window register offset
// Constants relating to APIC ID registers
#define APIC_ID_MASK 0xff000000
#define APIC_ID_SHIFT 24
#define APIC_ID_CLUSTER 0xf0
#define APIC_ID_CLUSTER_ID 0x0f
#define APIC_MAX_CLUSTER 0xe
#define APIC_MAX_INTRACLUSTER_ID 3
#define APIC_ID_CLUSTER_SHIFT 4
// Fields in VER
#define APIC_VER_VERSION 0x000000ff
#define APIC_VER_MAXLVT 0x00ff0000
#define MAXLVTSHIFT 16
// Indexes into IO APIC
#define IOAPIC_ID 0x00
#define IOAPIC_VER 0x01
#define IOAPIC_ARB 0x02
#define IOAPIC_REDTBL 0x10
#define IOAPIC_REDTBL0 IOAPIC_REDTBL
#define IOAPIC_REDTBL1 (IOAPIC_REDTBL+0x02)
#define IOAPIC_REDTBL2 (IOAPIC_REDTBL+0x04)
#define IOAPIC_REDTBL3 (IOAPIC_REDTBL+0x06)
#define IOAPIC_REDTBL4 (IOAPIC_REDTBL+0x08)
#define IOAPIC_REDTBL5 (IOAPIC_REDTBL+0x0a)
#define IOAPIC_REDTBL6 (IOAPIC_REDTBL+0x0c)
#define IOAPIC_REDTBL7 (IOAPIC_REDTBL+0x0e)
#define IOAPIC_REDTBL8 (IOAPIC_REDTBL+0x10)
#define IOAPIC_REDTBL9 (IOAPIC_REDTBL+0x12)
#define IOAPIC_REDTBL10 (IOAPIC_REDTBL+0x14)
#define IOAPIC_REDTBL11 (IOAPIC_REDTBL+0x16)
#define IOAPIC_REDTBL12 (IOAPIC_REDTBL+0x18)
#define IOAPIC_REDTBL13 (IOAPIC_REDTBL+0x1a)
#define IOAPIC_REDTBL14 (IOAPIC_REDTBL+0x1c)
#define IOAPIC_REDTBL15 (IOAPIC_REDTBL+0x1e)
#define IOAPIC_REDTBL16 (IOAPIC_REDTBL+0x20)
#define IOAPIC_REDTBL17 (IOAPIC_REDTBL+0x22)
#define IOAPIC_REDTBL18 (IOAPIC_REDTBL+0x24)
#define IOAPIC_REDTBL19 (IOAPIC_REDTBL+0x26)
#define IOAPIC_REDTBL20 (IOAPIC_REDTBL+0x28)
#define IOAPIC_REDTBL21 (IOAPIC_REDTBL+0x2a)
#define IOAPIC_REDTBL22 (IOAPIC_REDTBL+0x2c)
#define IOAPIC_REDTBL23 (IOAPIC_REDTBL+0x2e)
// Fields in the IO APIC's redirection table entries
#define IOART_DEST APIC_ID_MASK // broadcast addr: all APICs
#define IOART_RESV 0x00fe0000 // reserved
#define IOART_INTMASK 0x00010000 // R/W: INTerrupt mask
#define IOART_INTMCLR 0x00000000 // clear, allow INTs
#define IOART_INTMSET 0x00010000 // set, inhibit INTs
#define IOART_TRGRMOD 0x00008000 // R/W: trigger mode
#define IOART_TRGREDG 0x00000000 // edge
#define IOART_TRGRLVL 0x00008000 // level
#define IOART_REM_IRR 0x00004000 // RO: remote IRR
#define IOART_INTPOL 0x00002000 // R/W: INT input pin polarity
#define IOART_INTAHI 0x00000000 // active high
#define IOART_INTALO 0x00002000 // active low
#define IOART_DELIVS 0x00001000 // RO: delivery status
#define IOART_DESTMOD 0x00000800 // R/W: destination mode
#define IOART_DESTPHY 0x00000000 // physical
#define IOART_DESTLOG 0x00000800 // logical
#define IOART_DELMOD 0x00000700 // R/W: delivery mode
#define IOART_DELFIXED 0x00000000 // fixed
#define IOART_DELLOPRI 0x00000100 // lowest priority
#define IOART_DELSMI 0x00000200 // System Management INT
#define IOART_DELRSV1 0x00000300 // reserved
#define IOART_DELNMI 0x00000400 // NMI signal
#define IOART_DELINIT 0x00000500 // INIT signal
#define IOART_DELRSV2 0x00000600 // reserved
#define IOART_DELEXINT 0x00000700 // External INTerrupt
#define IOART_INTVEC 0x000000ff // R/W: INTerrupt vector field
// Fields in VER
#define IOART_VER_VERSION 0x000000ff
#define IOART_VER_MAXREDIR 0x00ff0000
#define MAXREDIRSHIFT 16

153
lapic.c
View file

@ -1,132 +1,91 @@
// The local APIC manages internal (non-I/O) interrupts.
// See Chapter 8 & Appendix C of Intel processor manual volume 3.
#include "types.h" #include "types.h"
#include "mp.h"
#include "defs.h" #include "defs.h"
#include "param.h" #include "param.h"
#include "x86.h" #include "x86.h"
#include "traps.h" #include "traps.h"
#include "mmu.h" #include "mmu.h"
#include "proc.h" #include "proc.h"
#include "lapic.h"
// Local APIC registers, divided by 4 for use as uint[] indices. // Local APIC registers, divided by 4 for use as uint[] indices.
#define ID (0x0020/4) // ID #define ID (0x0020/4) // ID
#define VER (0x0030/4) // Version #define VER (0x0030/4) // Version
#define TPR (0x0080/4) // Task Priority #define TPR (0x0080/4) // Task Priority
#define APR (0x0090/4) // Arbitration Priority
#define PPR (0x00A0/4) // Processor Priority
#define EOI (0x00B0/4) // EOI #define EOI (0x00B0/4) // EOI
#define LDR (0x00D0/4) // Logical Destination
#define DFR (0x00E0/4) // Destination Format
#define SVR (0x00F0/4) // Spurious Interrupt Vector #define SVR (0x00F0/4) // Spurious Interrupt Vector
#define ISR (0x0100/4) // Interrupt Status (8 registers) #define ENABLE 0x00000100 // Unit Enable
#define TMR (0x0180/4) // Trigger Mode (8 registers)
#define IRR (0x0200/4) // Interrupt Request (8 registers)
#define ESR (0x0280/4) // Error Status #define ESR (0x0280/4) // Error Status
#define ICRLO (0x0300/4) // Interrupt Command #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 LEVEL 0x00008000 // Level triggered
#define BCAST 0x00080000 // Send to all APICs, including self.
#define ICRHI (0x0310/4) // Interrupt Command [63:32] #define ICRHI (0x0310/4) // Interrupt Command [63:32]
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER) #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 PCINT (0x0340/4) // Performance Counter LVT
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0) #define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1) #define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR) #define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
#define MASKED 0x00010000 // Interrupt masked
#define TICR (0x0380/4) // Timer Initial Count #define TICR (0x0380/4) // Timer Initial Count
#define TCCR (0x0390/4) // Timer Current Count #define TCCR (0x0390/4) // Timer Current Count
#define TDCR (0x03E0/4) // Timer Divide Configuration #define TDCR (0x03E0/4) // Timer Divide Configuration
// SVR
#define ENABLE 0x00000100 // Unit Enable
#define FOCUS 0x00000200 // Focus Processor Checking Disable
// ICRLO
// [14] IPI Trigger Mode Level (RW)
#define DEASSERT 0x00000000 // Deassert level-sensitive interrupt
#define ASSERT 0x00004000 // Assert level-sensitive interrupt
// [17:16] Remote Read Status
#define INVALID 0x00000000 // Invalid
#define WAIT 0x00010000 // In-Progress
#define VALID 0x00020000 // Valid
// [19:18] Destination Shorthand
#define FIELD 0x00000000 // No shorthand
#define SELF 0x00040000 // Self is single destination
#define ALLINC 0x00080000 // All including self
#define ALLEXC 0x000C0000 // All Excluding self
// ESR
#define SENDCS 0x00000001 // Send CS Error
#define RCVCS 0x00000002 // Receive CS Error
#define SENDACCEPT 0x00000004 // Send Accept Error
#define RCVACCEPT 0x00000008 // Receive Accept Error
#define SENDVECTOR 0x00000020 // Send Illegal Vector
#define RCVVECTOR 0x00000040 // Receive Illegal Vector
#define REGISTER 0x00000080 // Illegal Register Address
// [17] Timer Mode (RW)
#define ONESHOT 0x00000000 // One-shot
#define PERIODIC 0x00020000 // Periodic
// [19:18] Timer Base (RW)
#define CLKIN 0x00000000 // use CLKIN as input
#define TMBASE 0x00040000 // use TMBASE
#define DIVIDER 0x00080000 // use output of the divider
#define X2 0x00000000 // divide by 2
#define X4 0x00000001 // divide by 4
#define X8 0x00000002 // divide by 8
#define X16 0x00000003 // divide by 16
#define X32 0x00000008 // divide by 32
#define X64 0x00000009 // divide by 64
#define X128 0x0000000A // divide by 128
#define X1 0x0000000B // divide by 1
//PAGEBREAK!
volatile uint *lapic; // Initialized in mp.c volatile uint *lapic; // Initialized in mp.c
//PAGEBREAK!
void void
lapic_init(int c) lapic_init(int c)
{ {
uint r, lvt;
if(!lapic) if(!lapic)
return; return;
lapic[DFR] = 0xFFFFFFFF; // Set dst format register // Enable local APIC; set spurious interrupt vector.
r = (lapic[ID]>>24) & 0xFF; // Read APIC ID
lapic[LDR] = (1<<r) << 24;
lapic[TPR] = 0xFF; // No interrupts for now
// Enable APIC
lapic[SVR] = ENABLE | (IRQ_OFFSET+IRQ_SPURIOUS); lapic[SVR] = ENABLE | (IRQ_OFFSET+IRQ_SPURIOUS);
// In virtual wire mode, set up the LINT0 and LINT1 as follows: // The timer repeatedly counts down at bus frequency
lapic[LINT0] = APIC_IMASK | APIC_EXTINT; // from lapic[TICR] and then issues an interrupt.
lapic[LINT1] = APIC_IMASK | APIC_NMI; // Lapic[TCCR] is the current counter value.
// If xv6 cared more about precise timekeeping, the
// values of TICR and TCCR would be calibrated using
// an external time source.
lapic[TDCR] = X1;
lapic[TICR] = 10000000;
lapic[TCCR] = 10000000;
lapic[TIMER] = PERIODIC | (IRQ_OFFSET + IRQ_TIMER);
lapic[EOI] = 0; // Ack any outstanding interrupts. // Disable logical interrupt lines.
lapic[LINT0] = MASKED;
lapic[LINT1] = MASKED;
lvt = (lapic[VER]>>16) & 0xFF; // Disable performance counter overflow interrupts
if(lvt >= 4) // on machines that provide that interrupt entry.
lapic[PCINT] = APIC_IMASK; if(((lapic[VER]>>16) & 0xFF) >= 4)
lapic[PCINT] = MASKED;
// Map error interrupt to IRQ_ERROR.
lapic[ERROR] = IRQ_OFFSET+IRQ_ERROR; lapic[ERROR] = IRQ_OFFSET+IRQ_ERROR;
lapic[ESR] = 0;
lapic[ESR];
// Issue an INIT Level De-Assert to synchronise arbitration ID's. // Clear error status register (requires back-to-back writes).
lapic[ESR] = 0;
lapic[ESR] = 0;
// Ack any outstanding interrupts.
lapic[EOI] = 0;
// Send an Init Level De-Assert to synchronise arbitration ID's.
lapic[ICRHI] = 0; lapic[ICRHI] = 0;
lapic[ICRLO] = ALLINC | APIC_LEVEL | lapic[ICRLO] = BCAST | INIT | LEVEL;
DEASSERT | APIC_INIT; while(lapic[ICRLO] & DELIVS)
while(lapic[ICRLO] & APIC_DELIVS)
; ;
// Initialize the interrupt timer. // Enable interrupts on the APIC (but not on the processor).
// On real hardware would need to do more XXX.
lapic[TDCR] = X1;
lapic[TIMER] = CLKIN | PERIODIC | (IRQ_OFFSET + IRQ_TIMER);
lapic[TCCR] = 10000000;
lapic[TICR] = 10000000;
// Enable interrupts on the APIC (but not on processor).
lapic[TPR] = 0; lapic[TPR] = 0;
} }
@ -146,22 +105,34 @@ lapic_eoi(void)
lapic[EOI] = 0; lapic[EOI] = 0;
} }
// Spin for a given number of microseconds.
// On real hardware would want to tune this dynamically.
static void
microdelay(int us)
{
volatile int j = 0;
while(us-- > 0)
for(j=0; j<10000; j++);
}
// Start additional processor running bootstrap code at addr. // Start additional processor running bootstrap code at addr.
// See Appendix B of MultiProcessor Specification.
void void
lapic_startap(uchar apicid, uint addr) lapic_startap(uchar apicid, uint addr)
{ {
int i; int i;
volatile int j = 0; volatile int j = 0;
// Send INIT interrupt to reset other CPU.
lapic[ICRHI] = apicid<<24; lapic[ICRHI] = apicid<<24;
lapic[ICRLO] = FIELD | APIC_LEVEL | ASSERT | APIC_INIT; lapic[ICRLO] = INIT | LEVEL;
for(j=0; j<10000; j++); // 200us microdelay(10);
lapic[ICRLO] = FIELD | APIC_LEVEL | DEASSERT | APIC_INIT;
for(j=0; j<1000000; j++); // 10ms
// Send startup IPI (twice!) to enter bootstrap code.
for(i = 0; i < 2; i++){ for(i = 0; i < 2; i++){
lapic[ICRHI] = apicid<<24; lapic[ICRHI] = apicid<<24;
lapic[ICRLO] = FIELD | APIC_EDGE | APIC_STARTUP | (addr/4096); lapic[ICRLO] = STARTUP | (addr>>12);
for(j=0; j<10000; j++); // 200us for(j=0; j<10000; j++); // 200us
} }
} }

34
main.c
View file

@ -12,6 +12,8 @@
extern char edata[], end[]; extern char edata[], end[];
void bootothers(void);
// Bootstrap processor starts running C code here. // Bootstrap processor starts running C code here.
// This is called main0 not main so that it can have // This is called main0 not main so that it can have
// a void return type. Gcc can't handle functions named // a void return type. Gcc can't handle functions named
@ -37,7 +39,7 @@ main0(void)
asm volatile("movl %0, %%ebp" : : "r" (cpus[bcpu].mpstack+MPSTACK)); asm volatile("movl %0, %%ebp" : : "r" (cpus[bcpu].mpstack+MPSTACK));
lapic_init(bcpu); lapic_init(bcpu);
cprintf("\ncpu%d: starting xv6\n\n", cpu()); cprintf("\\ncpu%d: starting xv6\\n\\n", cpu());
pinit(); // process table pinit(); // process table
binit(); // buffer cache binit(); // buffer cache
@ -51,7 +53,7 @@ main0(void)
setupsegs(0); // segments & TSS setupsegs(0); // segments & TSS
console_init(); // I/O devices & their interrupts console_init(); // I/O devices & their interrupts
ide_init(); // disk ide_init(); // disk
mp_startthem(); // other CPUs bootothers(); // boot other CPUs
if(!ismp) if(!ismp)
pit8253_timerinit(); // uniprocessor timer pit8253_timerinit(); // uniprocessor timer
userinit(); // first user process userinit(); // first user process
@ -67,7 +69,7 @@ main0(void)
void void
mpmain(void) mpmain(void)
{ {
cprintf("cpu%d: starting\n", cpu()); cprintf("cpu%d: starting\\n", cpu());
idtinit(); idtinit();
lapic_init(cpu()); lapic_init(cpu());
setupsegs(0); setupsegs(0);
@ -82,3 +84,29 @@ mpmain(void)
scheduler(); scheduler();
} }
void
bootothers(void)
{
extern uchar _binary_bootother_start[], _binary_bootother_size[];
uchar *code;
struct cpu *c;
// Write bootstrap code to unused memory at 0x7000.
code = (uchar*)0x7000;
memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
for(c = cpus; c < cpus+ncpu; c++){
if(c == cpus+cpu()) // We've started already.
continue;
// Set target %esp, %eip
*(void**)(code-4) = c->mpstack + MPSTACK;
*(void**)(code-8) = mpmain;
lapic_startap(c->apicid, (uint)code);
// Wait for cpu to get through bootstrap.
while(c->booted == 0)
;
}
}

200
mp.c
View file

@ -1,3 +1,5 @@
// http://developer.intel.com/design/pentium/datashts/24201606.pdf
#include "types.h" #include "types.h"
#include "mp.h" #include "mp.h"
#include "defs.h" #include "defs.h"
@ -7,52 +9,39 @@
#include "mmu.h" #include "mmu.h"
#include "proc.h" #include "proc.h"
static char *buses[] = {
"CBUSI ",
"CBUSII",
"EISA ",
"FUTURE",
"INTERN",
"ISA ",
"MBI ",
"MBII ",
"MCA ",
"MPI ",
"MPSA ",
"NUBUS ",
"PCI ",
"PCMCIA",
"TC ",
"VL ",
"VME ",
"XPRESS",
0,
};
struct cpu cpus[NCPU]; struct cpu cpus[NCPU];
static struct cpu *bcpu;
int ismp; int ismp;
int ncpu; int ncpu;
uchar ioapic_id; uchar ioapic_id;
static struct cpu *bcpu; int
static struct mp *mp; // The floating MP structure mp_bcpu(void)
static struct mp*
mp_scan(uchar *addr, int len)
{ {
uchar *e, *p, sum; return bcpu-cpus;
int i; }
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*
mp_search1(uchar *addr, int len)
{
uchar *e, *p;
e = addr+len; 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)) if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
continue;
sum = 0;
for(i = 0; i < sizeof(struct mp); i++)
sum += p[i];
if(sum == 0)
return (struct mp*)p; return (struct mp*)p;
}
return 0; return 0;
} }
@ -70,14 +59,14 @@ mp_search(void)
bda = (uchar*)0x400; bda = (uchar*)0x400;
if((p = (bda[0x0F]<<8)|bda[0x0E])){ if((p = (bda[0x0F]<<8)|bda[0x0E])){
if((mp = mp_scan((uchar*) p, 1024))) if((mp = mp_search1((uchar*)p, 1024)))
return mp; return mp;
}else{ }else{
p = ((bda[0x14]<<8)|bda[0x13])*1024; p = ((bda[0x14]<<8)|bda[0x13])*1024;
if((mp = mp_scan((uchar*)p-1024, 1024))) if((mp = mp_search1((uchar*)p-1024, 1024)))
return mp; return mp;
} }
return mp_scan((uchar*)0xF0000, 0x10000); return mp_search1((uchar*)0xF0000, 0x10000);
} }
// Search for an MP configuration table. For now, // Search for an MP configuration table. For now,
@ -85,93 +74,64 @@ mp_search(void)
// Check for correct signature, calculate the checksum and, // Check for correct signature, calculate the checksum and,
// if correct, check the version. // if correct, check the version.
// To do: check extended table checksum. // To do: check extended table checksum.
static int static struct mpconf*
mp_detect(void) mp_config(struct mp **pmp)
{ {
struct mpctb *pcmp; struct mpconf *conf;
uchar *p, sum; struct mp *mp;
uint length;
if((mp = mp_search()) == 0 || mp->physaddr == 0) if((mp = mp_search()) == 0 || mp->physaddr == 0)
return -1;
pcmp = (struct mpctb*) mp->physaddr;
if(memcmp(pcmp, "PCMP", 4) != 0)
return -1;
if(pcmp->version != 1 && pcmp->version != 4)
return -1;
length = pcmp->length;
sum = 0;
for(p = (uchar*)pcmp; length; length--)
sum += *p++;
if(sum != 0)
return -1;
return 0; return 0;
conf = (struct mpconf*)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 void
mp_init(void) mp_init(void)
{ {
int i;
uchar *p, *e; uchar *p, *e;
struct mpctb *mpctb; struct mp *mp;
struct mppe *proc; struct mpconf *conf;
struct mpbe *bus; struct mpproc *proc;
struct mpioapic *ioapic; struct mpioapic *ioapic;
struct mpie *intr;
ncpu = 0; bcpu = &cpus[ncpu];
if(mp_detect() < 0) if((conf = mp_config(&mp)) == 0)
return; return;
ismp = 1; ismp = 1;
lapic = (uint*)conf->lapicaddr;
// Run through the table saving information needed for starting for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){
// application processors and initialising any I/O APICs. The table
// is guaranteed to be in order such that only one pass is necessary.
mpctb = (struct mpctb*)mp->physaddr;
lapic = (uint*)mpctb->lapicaddr;
p = (uchar*)mpctb + sizeof(*mpctb);
e = (uchar*)mpctb + mpctb->length;
while(p < e) {
switch(*p){ switch(*p){
case MPPROCESSOR: case MPPROC:
proc = (struct mppe*) p; proc = (struct mpproc*)p;
cpus[ncpu].apicid = proc->apicid; cpus[ncpu].apicid = proc->apicid;
if(proc->flags & MPBP) { if(proc->flags & MPBOOT)
bcpu = &cpus[ncpu]; bcpu = &cpus[ncpu];
}
ncpu++; ncpu++;
p += sizeof(struct mppe); p += sizeof(struct mpproc);
continue;
case MPBUS:
bus = (struct mpbe*) p;
for(i = 0; buses[i]; i++){
if(strncmp(buses[i], bus->string, sizeof(bus->string)) == 0)
break;
}
p += sizeof(struct mpbe);
continue; continue;
case MPIOAPIC: case MPIOAPIC:
ioapic = (struct mpioapic*)p; ioapic = (struct mpioapic*)p;
ioapic_id = ioapic->apicno; ioapic_id = ioapic->apicno;
p += sizeof(struct mpioapic); p += sizeof(struct mpioapic);
continue; continue;
case MPBUS:
case MPIOINTR: case MPIOINTR:
intr = (struct mpie*) p; case MPLINTR:
p += sizeof(struct mpie); p += 8;
continue; continue;
default: default:
cprintf("mp_init: unknown PCMP type 0x%x (e-p 0x%x)\n", *p, e-p); cprintf("mp_init: unknown config type %x\n", *p);
while(p < e){ panic("mp_init");
cprintf("%uX ", *p);
p++;
}
break;
} }
} }
@ -182,47 +142,3 @@ mp_init(void)
outb(0x23, inb(0x23) | 1); // Mask external interrupts. outb(0x23, inb(0x23) | 1); // Mask external interrupts.
} }
} }
int
mp_bcpu(void)
{
if(ismp)
return bcpu-cpus;
return 0;
}
extern void mpmain(void);
// Write bootstrap code to unused memory at 0x7000.
#define APBOOTCODE 0x7000
void
mp_startthem(void)
{
extern uchar _binary_bootother_start[], _binary_bootother_size[];
extern int main();
int c;
memmove((void*) APBOOTCODE,_binary_bootother_start,
(uint) _binary_bootother_size);
for(c = 0; c < ncpu; c++){
// Our current cpu has already started.
if(c == cpu())
continue;
// Set target %esp
*(uint*)(APBOOTCODE-4) = (uint) (cpus[c].mpstack) + MPSTACK;
// Set target %eip
*(uint*)(APBOOTCODE-8) = (uint)mpmain;
// Go!
lapic_startap(cpus[c].apicid, (uint)APBOOTCODE);
// Wait for cpu to get through bootstrap.
while(cpus[c].booted == 0)
;
}
}

84
mp.h
View file

@ -1,4 +1,4 @@
// See MultiProcessor Specification Version 1.[14]. // See MultiProcessor Specification Version 1.[14]
struct mp { // floating pointer struct mp { // floating pointer
uchar signature[4]; // "_MP_" uchar signature[4]; // "_MP_"
@ -11,7 +11,7 @@ struct mp { // floating pointer
uchar reserved[3]; uchar reserved[3];
}; };
struct mpctb { // configuration table header struct mpconf { // configuration table header
uchar signature[4]; // "PCMP" uchar signature[4]; // "PCMP"
ushort length; // total table length ushort length; // total table length
uchar version; // [14] uchar version; // [14]
@ -26,22 +26,17 @@ struct mpctb { // configuration table header
uchar reserved; uchar reserved;
}; };
struct mppe { // processor table entry struct mpproc { // processor table entry
uchar type; // entry type (0) uchar type; // entry type (0)
uchar apicid; // local APIC id uchar apicid; // local APIC id
uchar version; // local APIC verison uchar version; // local APIC verison
uchar flags; // CPU flags uchar flags; // CPU flags
#define MPBOOT 0x02 // This proc is the bootstrap processor.
uchar signature[4]; // CPU signature uchar signature[4]; // CPU signature
uint feature; // feature flags from CPUID instruction uint feature; // feature flags from CPUID instruction
uchar reserved[8]; uchar reserved[8];
}; };
struct mpbe { // bus table entry
uchar type; // entry type (1)
uchar busno; // bus id
char string[6]; // bus type string
};
struct mpioapic { // I/O APIC table entry struct mpioapic { // I/O APIC table entry
uchar type; // entry type (2) uchar type; // entry type (2)
uchar apicno; // I/O APIC id uchar apicno; // I/O APIC id
@ -50,69 +45,10 @@ struct mpioapic { // I/O APIC table entry
uint *addr; // I/O APIC address uint *addr; // I/O APIC address
}; };
struct mpie { // interrupt table entry // Table entry types
uchar type; // entry type ([34]) #define MPPROC 0x00 // One per processor
uchar intr; // interrupt type #define MPBUS 0x01 // One per bus
ushort flags; // interrupt flag #define MPIOAPIC 0x02 // One per I/O APIC
uchar busno; // source bus id #define MPIOINTR 0x03 // One per bus interrupt source
uchar irq; // source bus irq #define MPLINTR 0x04 // One per system interrupt source
uchar apicno; // destination APIC id
uchar intin; // destination APIC [L]INTIN#
};
enum { // table entry types
MPPROCESSOR = 0x00, // one entry per processor
MPBUS = 0x01, // one entry per bus
MPIOAPIC = 0x02, // one entry per I/O APIC
MPIOINTR = 0x03, // one entry per bus interrupt source
MPLINTR = 0x04, // one entry per system interrupt source
MPSASM = 0x80,
MPHIERARCHY = 0x81,
MPCBASM = 0x82,
// PCMPprocessor and PCMPioapic flags
MPEN = 0x01, // enabled
MPBP = 0x02, // bootstrap processor
// PCMPiointr and PCMPlintr flags
MPPOMASK = 0x03, // polarity conforms to bus specs
MPHIGH = 0x01, // active high
MPLOW = 0x03, // active low
MPELMASK = 0x0C, // trigger mode of APIC input signals
MPEDGE = 0x04, // edge-triggered
MPLEVEL = 0x0C, // level-triggered
// PCMPiointr and PCMPlintr interrupt type
MPINT = 0x00, // vectored interrupt from APIC Rdt
MPNMI = 0x01, // non-maskable interrupt
MPSMI = 0x02, // system management interrupt
MPExtINT = 0x03, // vectored interrupt from external PIC
};
// Common bits for
// I/O APIC Redirection Table Entry;
// Local APIC Local Interrupt Vector Table;
// Local APIC Inter-Processor Interrupt;
// Local APIC Timer Vector Table.
enum {
APIC_FIXED = 0x00000000, // [10:8] Delivery Mode
APIC_LOWEST = 0x00000100, // Lowest priority
APIC_SMI = 0x00000200, // System Management Interrupt
APIC_RR = 0x00000300, // Remote Read
APIC_NMI = 0x00000400,
APIC_INIT = 0x00000500, // INIT/RESET
APIC_STARTUP = 0x00000600, // Startup IPI
APIC_EXTINT = 0x00000700,
APIC_PHYSICAL = 0x00000000, // [11] Destination Mode (RW)
APIC_LOGICAL = 0x00000800,
APIC_DELIVS = 0x00001000, // [12] Delivery Status (RO)
APIC_HIGH = 0x00000000, // [13] Interrupt Input Pin Polarity (RW)
APIC_LOW = 0x00002000,
APIC_REMOTEIRR = 0x00004000, // [14] Remote IRR (RO)
APIC_EDGE = 0x00000000, // [15] Trigger Mode (RW)
APIC_LEVEL = 0x00008000,
APIC_IMASK = 0x00010000, // [16] Interrupt Mask
};