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 "mp.h"
#include "defs.h"
#include "x86.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 {
uint ioregsel; uint p01; uint p02; uint p03;
uint iowin; uint p11; uint p12; uint p13;
uint reg;
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
ioapic_read(struct ioapic *io, int reg)
ioapic_read(int reg)
{
io->ioregsel = reg;
return io->iowin;
ioapic->reg = reg;
return ioapic->data;
}
static void
ioapic_write(struct ioapic *io, int reg, uint val)
ioapic_write(int reg, uint data)
{
io->ioregsel = reg;
io->iowin = val;
ioapic->reg = reg;
ioapic->data = data;
}
void
ioapic_init(void)
{
struct ioapic *io;
uint l, h;
int nintr;
uchar id;
int i;
int i, id, maxintr;
if(!ismp)
return;
io = (struct ioapic*) IO_APIC_BASE;
l = ioapic_read(io, IOAPIC_VER);
nintr = ((l & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1;
id = ioapic_read(io, IOAPIC_ID) >> APIC_ID_SHIFT;
ioapic = (volatile struct ioapic*)IOAPIC;
maxintr = (ioapic_read(REG_VER) >> 16) & 0xFF;
id = ioapic_read(REG_ID) >> 24;
if(id != ioapic_id)
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
// Assume that pin 0 on the first I/O APIC is an ExtINT pin.
// Assume that pins 1-15 are ISA interrupts
l = ioapic_read(io, IOAPIC_REDTBL_LO(i));
l = l & ~IOART_INTMASK; // allow INTs
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);
// Mark all interrupts edge-triggered, active high, disabled,
// and not routed to any CPUs.
for(i = 0; i <= maxintr; i++){
ioapic_write(REG_TABLE+2*i, INT_DISABLED | (IRQ_OFFSET + i));
ioapic_write(REG_TABLE+2*i+1, 0);
}
}
void
ioapic_enable (int irq, int cpunum)
ioapic_enable(int irq, int cpunum)
{
uint l, h;
struct ioapic *io;
if(!ismp)
return;
io = (struct ioapic*) IO_APIC_BASE;
l = ioapic_read(io, IOAPIC_REDTBL_LO(irq));
l = l & ~IOART_INTMASK; // allow INTs
ioapic_write(io, IOAPIC_REDTBL_LO(irq), l);
h = ioapic_read(io, IOAPIC_REDTBL_HI(irq));
h &= ~IOART_DEST;
h |= (cpunum << APIC_ID_SHIFT);
ioapic_write(io, IOAPIC_REDTBL_HI(irq), h);
// Mark interrupt edge-triggered, active high,
// enabled, and routed to the given cpunum,
// which happens to be that cpu's APIC ID.
ioapic_write(REG_TABLE+2*irq, IRQ_OFFSET + irq);
ioapic_write(REG_TABLE+2*irq+1, cpunum << 24);
}

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

155
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 "mp.h"
#include "defs.h"
#include "param.h"
#include "x86.h"
#include "traps.h"
#include "mmu.h"
#include "proc.h"
#include "lapic.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 APR (0x0090/4) // Arbitration Priority
#define PPR (0x00A0/4) // Processor Priority
#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 ISR (0x0100/4) // Interrupt Status (8 registers)
#define TMR (0x0180/4) // Trigger Mode (8 registers)
#define IRR (0x0200/4) // Interrupt Request (8 registers)
#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 LEVEL 0x00008000 // Level triggered
#define BCAST 0x00080000 // Send to all APICs, including self.
#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
// 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
//PAGEBREAK!
void
lapic_init(int c)
{
uint r, lvt;
if(!lapic)
return;
lapic[DFR] = 0xFFFFFFFF; // Set dst format register
r = (lapic[ID]>>24) & 0xFF; // Read APIC ID
lapic[LDR] = (1<<r) << 24;
lapic[TPR] = 0xFF; // No interrupts for now
// Enable APIC
// Enable local APIC; set spurious interrupt vector.
lapic[SVR] = ENABLE | (IRQ_OFFSET+IRQ_SPURIOUS);
// In virtual wire mode, set up the LINT0 and LINT1 as follows:
lapic[LINT0] = APIC_IMASK | APIC_EXTINT;
lapic[LINT1] = APIC_IMASK | APIC_NMI;
// The timer repeatedly counts down at bus frequency
// from lapic[TICR] and then issues an interrupt.
// 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;
if(lvt >= 4)
lapic[PCINT] = APIC_IMASK;
// Disable performance counter overflow interrupts
// on machines that provide that interrupt entry.
if(((lapic[VER]>>16) & 0xFF) >= 4)
lapic[PCINT] = MASKED;
// Map error interrupt to 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[ICRLO] = ALLINC | APIC_LEVEL |
DEASSERT | APIC_INIT;
while(lapic[ICRLO] & APIC_DELIVS)
lapic[ICRLO] = BCAST | INIT | LEVEL;
while(lapic[ICRLO] & DELIVS)
;
// Initialize the interrupt timer.
// 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).
// Enable interrupts on the APIC (but not on the processor).
lapic[TPR] = 0;
}
@ -146,22 +105,34 @@ lapic_eoi(void)
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.
// See Appendix B of MultiProcessor Specification.
void
lapic_startap(uchar apicid, uint addr)
{
int i;
volatile int j = 0;
// Send INIT interrupt to reset other CPU.
lapic[ICRHI] = apicid<<24;
lapic[ICRLO] = FIELD | APIC_LEVEL | ASSERT | APIC_INIT;
for(j=0; j<10000; j++); // 200us
lapic[ICRLO] = FIELD | APIC_LEVEL | DEASSERT | APIC_INIT;
for(j=0; j<1000000; j++); // 10ms
lapic[ICRLO] = INIT | LEVEL;
microdelay(10);
// Send startup IPI (twice!) to enter bootstrap code.
for(i = 0; i < 2; i++){
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
}
}

34
main.c
View file

@ -12,6 +12,8 @@
extern char edata[], end[];
void bootothers(void);
// Bootstrap processor starts running C code here.
// This is called main0 not main so that it can have
// 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));
lapic_init(bcpu);
cprintf("\ncpu%d: starting xv6\n\n", cpu());
cprintf("\\ncpu%d: starting xv6\\n\\n", cpu());
pinit(); // process table
binit(); // buffer cache
@ -51,7 +53,7 @@ main0(void)
setupsegs(0); // segments & TSS
console_init(); // I/O devices & their interrupts
ide_init(); // disk
mp_startthem(); // other CPUs
bootothers(); // boot other CPUs
if(!ismp)
pit8253_timerinit(); // uniprocessor timer
userinit(); // first user process
@ -67,7 +69,7 @@ main0(void)
void
mpmain(void)
{
cprintf("cpu%d: starting\n", cpu());
cprintf("cpu%d: starting\\n", cpu());
idtinit();
lapic_init(cpu());
setupsegs(0);
@ -82,3 +84,29 @@ mpmain(void)
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)
;
}
}

208
mp.c
View file

@ -1,3 +1,5 @@
// http://developer.intel.com/design/pentium/datashts/24201606.pdf
#include "types.h"
#include "mp.h"
#include "defs.h"
@ -7,52 +9,39 @@
#include "mmu.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];
static struct cpu *bcpu;
int ismp;
int ncpu;
uchar ioapic_id;
static struct cpu *bcpu;
static struct mp *mp; // The floating MP structure
static struct mp*
mp_scan(uchar *addr, int len)
int
mp_bcpu(void)
{
uchar *e, *p, sum;
int i;
return bcpu-cpus;
}
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;
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++)
sum += p[i];
if(sum == 0)
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;
}
@ -68,110 +57,81 @@ mp_search(void)
uint p;
struct mp *mp;
bda = (uchar*) 0x400;
bda = (uchar*)0x400;
if((p = (bda[0x0F]<<8)|bda[0x0E])){
if((mp = mp_scan((uchar*) p, 1024)))
if((mp = mp_search1((uchar*)p, 1024)))
return mp;
}else{
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_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,
// 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 int
mp_detect(void)
static struct mpconf*
mp_config(struct mp **pmp)
{
struct mpctb *pcmp;
uchar *p, sum;
uint length;
struct mpconf *conf;
struct mp *mp;
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
mp_init(void)
{
int i;
uchar *p, *e;
struct mpctb *mpctb;
struct mppe *proc;
struct mpbe *bus;
struct mp *mp;
struct mpconf *conf;
struct mpproc *proc;
struct mpioapic *ioapic;
struct mpie *intr;
ncpu = 0;
if(mp_detect() < 0)
bcpu = &cpus[ncpu];
if((conf = mp_config(&mp)) == 0)
return;
ismp = 1;
lapic = (uint*)conf->lapicaddr;
// 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.
mpctb = (struct mpctb*)mp->physaddr;
lapic = (uint*)mpctb->lapicaddr;
p = (uchar*)mpctb + sizeof(*mpctb);
e = (uchar*)mpctb + mpctb->length;
while(p < e) {
for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){
switch(*p){
case MPPROCESSOR:
proc = (struct mppe*) p;
case MPPROC:
proc = (struct mpproc*)p;
cpus[ncpu].apicid = proc->apicid;
if(proc->flags & MPBP) {
if(proc->flags & MPBOOT)
bcpu = &cpus[ncpu];
}
ncpu++;
p += sizeof(struct mppe);
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);
p += sizeof(struct mpproc);
continue;
case MPIOAPIC:
ioapic = (struct mpioapic*) p;
ioapic = (struct mpioapic*)p;
ioapic_id = ioapic->apicno;
p += sizeof(struct mpioapic);
continue;
case MPBUS:
case MPIOINTR:
intr = (struct mpie*) p;
p += sizeof(struct mpie);
case MPLINTR:
p += 8;
continue;
default:
cprintf("mp_init: unknown PCMP type 0x%x (e-p 0x%x)\n", *p, e-p);
while(p < e){
cprintf("%uX ", *p);
p++;
}
break;
cprintf("mp_init: unknown config type %x\n", *p);
panic("mp_init");
}
}
@ -182,47 +142,3 @@ mp_init(void)
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
uchar signature[4]; // "_MP_"
@ -11,7 +11,7 @@ struct mp { // floating pointer
uchar reserved[3];
};
struct mpctb { // configuration table header
struct mpconf { // configuration table header
uchar signature[4]; // "PCMP"
ushort length; // total table length
uchar version; // [14]
@ -26,22 +26,17 @@ struct mpctb { // configuration table header
uchar reserved;
};
struct mppe { // processor table entry
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 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
uchar type; // entry type (2)
uchar apicno; // I/O APIC id
@ -50,69 +45,10 @@ struct mpioapic { // I/O APIC table entry
uint *addr; // I/O APIC address
};
struct mpie { // interrupt table entry
uchar type; // entry type ([34])
uchar intr; // interrupt type
ushort flags; // interrupt flag
uchar busno; // source bus id
uchar irq; // source bus irq
uchar apicno; // destination APIC id
uchar intin; // destination APIC [L]INTIN#
};
// 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
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
};