xv6/mp.c
rsc 5ce9751cab Changes to allow use of native x86 ELF compilers, which on my
Linux 2.4 box using gcc 3.4.6 don't seem to follow the same
conventions as the i386-jos-elf-gcc compilers.
Can run make 'TOOLPREFIX=' or edit the Makefile.

curproc[cpu()] can now be NULL, indicating that no proc is running.
This seemed safer to me than having curproc[0] and curproc[1]
both pointing at proc[0] potentially.

The old implementation of swtch depended on the stack frame layout
used inside swtch being okay to return from on the other stack
(exactly the V6 you are not expected to understand this).
It also could be called in two contexts: at boot time, to schedule
the very first process, and later, on behalf of a process, to sleep
or schedule some other process.

I split this into two functions: scheduler and swtch.

The scheduler is now a separate never-returning function, invoked
by each cpu once set up.  The scheduler looks like:

	scheduler() {
		setjmp(cpu.context);

		pick proc to schedule
		blah blah blah

		longjmp(proc.context)
	}

The new swtch is intended to be called only when curproc[cpu()] is not NULL,
that is, only on behalf of a user proc.  It does:

	swtch() {
		if(setjmp(proc.context) == 0)
			longjmp(cpu.context)
	}

to save the current proc context and then jump over to the scheduler,
running on the cpu stack.

Similarly the system call stubs are now in assembly in usys.S to avoid
needing to know the details of stack frame layout used by the compiler.

Also various changes in the debugging prints.
2006-07-11 01:07:40 +00:00

405 lines
10 KiB
C

#include "types.h"
#include "mp.h"
#include "defs.h"
#include "memlayout.h"
#include "param.h"
#include "x86.h"
#include "traps.h"
#include "mmu.h"
#include "proc.h"
/*
* 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 */
};
static char* buses[] = {
"CBUSI ",
"CBUSII",
"EISA ",
"FUTURE",
"INTERN",
"ISA ",
"MBI ",
"MBII ",
"MCA ",
"MPI ",
"MPSA ",
"NUBUS ",
"PCI ",
"PCMCIA",
"TC ",
"VL ",
"VME ",
"XPRESS",
0,
};
#define APBOOTCODE 0x7000 // XXX hack
static struct MP* mp; // The MP floating point structure
static uint32_t *lapicaddr;
struct cpu cpus[NCPU];
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;
}
void
lapic_timerinit()
{
cprintf("%d: init timer\n", cpu());
lapic_write(LAPIC_TDCR, LAPIC_X1);
lapic_write(LAPIC_TIMER, LAPIC_CLKIN | LAPIC_PERIODIC | (IRQ_OFFSET + IRQ_TIMER));
lapic_write(LAPIC_TCCR, 10000000);
lapic_write(LAPIC_TICR, 10000000);
}
void
lapic_timerintr()
{
cprintf("%d: timer interrupt!\n", cpu());
lapic_write (LAPIC_EOI, 0);
}
void
lapic_init(int c)
{
uint32_t r, lvt;
cprintf("lapic_init %d\n", c);
lapic_write(LAPIC_DFR, 0xFFFFFFFF); // set destination format register
r = (lapic_read(LAPIC_ID)>>24) & 0xFF; // read APIC ID
lapic_write(LAPIC_LDR, (1<<r)<<24); // set logical destination register to r
lapic_write(LAPIC_TPR, 0xFF); // no interrupts for now
lapic_write(LAPIC_SVR, LAPIC_ENABLE|(IRQ_OFFSET+IRQ_SPURIOUS)); // enable APIC
// in virtual wire mode, set up the LINT0 and LINT1 as follows:
lapic_write(LAPIC_LINT0, APIC_IMASK | APIC_EXTINT);
lapic_write(LAPIC_LINT1, APIC_IMASK | APIC_NMI);
lapic_write(LAPIC_EOI, 0); // acknowledge any outstanding interrupts.
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)
;
cprintf("Done init of an apic\n");
}
void
lapic_enableintr(void)
{
lapic_write(LAPIC_TPR, 0);
}
void
lapic_disableintr(void)
{
lapic_write(LAPIC_TPR, 0xFF);
}
int
cpu(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;
int i;
cprintf("scanning: 0x%x\n", (uint32_t)addr);
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)
return (struct MP *)p;
}
return 0;
}
static struct MP*
mp_search(void)
{
uint8_t *bda;
uint32_t p;
struct MP *mp;
/*
* Search for the MP Floating Pointer Structure, which according to the
* spec is in one of the following three locations:
* 1) in the first KB of the EBDA;
* 2) in the last KB of system base memory;
* 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
*/
bda = (uint8_t*) 0x400;
if((p = (bda[0x0F]<<8)|bda[0x0E])){
if((mp = mp_scan((uint8_t*) p, 1024)))
return mp;
}
else{
p = ((bda[0x14]<<8)|bda[0x13])*1024;
if((mp = mp_scan((uint8_t*)p-1024, 1024)))
return mp;
}
return mp_scan((uint8_t*)0xF0000, 0x10000);
}
static int
mp_detect(void)
{
struct MPCTB *pcmp;
uint8_t *p, sum;
uint32_t length;
/*
* 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.
*/
if((mp = mp_search()) == 0 || mp->physaddr == 0)
return 1;
pcmp = (struct MPCTB *) mp->physaddr;
if(memcmp(pcmp, "PCMP", 4))
return 2;
length = pcmp->length;
sum = 0;
for(p = (uint8_t*)pcmp; length; length--)
sum += *p++;
if(sum || (pcmp->version != 1 && pcmp->version != 4))
return 3;
cprintf("Mp spec rev #: %x imcrp 0x%x\n", mp->specrev, mp->imcrp);
return 0;
}
int
mp_isbcpu()
{
if (bcpu == 0) return 1;
else return 0;
}
void
mp_init()
{
int r;
uint8_t *p, *e;
struct MPCTB *mpctb;
struct MPPE *proc;
struct MPBE *bus;
int c;
extern int main();
int i;
ncpu = 0;
if ((r = mp_detect()) != 0) return;
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.
*/
mpctb = (struct MPCTB *) mp->physaddr;
lapicaddr = (uint32_t *) 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 MPPROCESSOR:
proc = (struct MPPE *) p;
cpus[ncpu].apicid = proc->apicid;
cprintf("a processor %x\n", cpus[ncpu].apicid);
if (proc->flags & MPBP) {
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;
}
cprintf("a bus %d\n", i);
p += sizeof(struct MPBE);
continue;
case MPIOAPIC:
cprintf("an I/O APIC\n");
p += sizeof(struct MPIOAPIC);
continue;
case MPIOINTR:
cprintf("an I/O intr\n");
p += sizeof(struct MPIE);
continue;
default:
cprintf("mpinit: unknown PCMP type 0x%x (e-p 0x%x)\n", *p, e-p);
while(p < e){
cprintf("%uX ", *p);
p++;
}
break;
}
}
lapic_init(bcpu-cpus);
cprintf("ncpu: %d boot %d\n", ncpu, bcpu-cpus);
extern uint8_t _binary_bootother_start[], _binary_bootother_size[];
memmove((void *) APBOOTCODE,_binary_bootother_start,
(uint32_t) _binary_bootother_size);
acquire_spinlock(&kernel_lock);
for(c = 0; c < ncpu; c++){
if (cpus+c == bcpu) continue;
cprintf ("starting processor %d\n", c);
release_grant_spinlock(&kernel_lock, c);
*(unsigned *)(APBOOTCODE-4) = (unsigned) (cpus[c].mpstack) + MPSTACK; // tell it what to use for %esp
*(unsigned *)(APBOOTCODE-8) = (unsigned)&main; // tell it where to jump to
lapic_startap(cpus + c, (uint32_t) APBOOTCODE);
acquire_spinlock(&kernel_lock);
cprintf ("done starting processor %d\n", c);
}
}