Simplify MP hardware code.
Mainly delete unused constants and code. Move mp_startthem to main.c as bootothers.
This commit is contained in:
parent
b63bb0fd00
commit
99b11b6c64
6 changed files with 213 additions and 453 deletions
97
ioapic.c
97
ioapic.c
|
@ -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);
|
||||
}
|
||||
|
|
88
ioapic.h
88
ioapic.h
|
@ -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
155
lapic.c
|
@ -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
34
main.c
|
@ -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
208
mp.c
|
@ -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
84
mp.h
|
@ -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
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue