shithub: vmxsmp

ref: e0e889fb69a6d4d3f2332244ef4f79f900a66d84
dir: /mptable.c/

View raw version
/*
 * mptable.c - MP Specification table generation for vmx SMP
 *
 * MP Floating Pointer: 16 bytes, signature "_MP_"
 * MP Config Header: 44 bytes, signature "PCMP"
 * Followed by variable entries:
 *   - Processor: 20 bytes, type 0
 *   - Bus: 8 bytes, type 1
 *   - I/O APIC: 8 bytes, type 2
 *   - I/O Interrupt: 8 bytes, type 3
 *   - Local Interrupt: 8 bytes, type 4
 *
 * Bus Layout (matching typical PC):
 *   Bus 0: PCI (primary PCI bus)
 *   Bus 1: ISA (behind PCI-ISA bridge)
 */

#include <u.h>
#include <libc.h>
#include <thread.h>
#include "dat.h"
#include "fns.h"

/* MP table entry types */
enum {
	MP_PROCESSOR	= 0,
	MP_BUS		= 1,
	MP_IOAPIC	= 2,
	MP_IOINTR	= 3,
	MP_LINTR	= 4,
};

/* Bus IDs - must be consistent throughout */
enum {
	BUS_PCI		= 0,	/* Primary PCI bus */
	BUS_ISA		= 1,	/* ISA bus (behind PCI-ISA bridge) */
};

static uchar
mpchecksum(uchar *p, int len)
{
	uchar sum = 0;
	while(len-- > 0)
		sum += *p++;
	return sum;
}

/*
 * Generate MP tables in guest memory at 0xF0000
 */
void
mpmktable(void)
{
	uchar *base, *fp, *cfg, *p, *entrystart;
	int i, ncpu, entries;
	u16int tbllen;
	
	base = gptr(0xF0000, 0x1000);
	if(base == nil){
		vmerror("mpmktable: cannot map 0xF0000");
		return;
	}
	
	/* Clear the area first */
	memset(base, 0, 0x1000);
	
	ncpu = nvcpu;
	fp = base;
	cfg = base + 0x40;  /* Config table at physical 0xF0040 */
	
	/*
	 * MP Floating Pointer Structure (16 bytes)
	 */
	fp[0] = '_';
	fp[1] = 'M';
	fp[2] = 'P';
	fp[3] = '_';
	/* Physical address 0x000F0040 in little-endian */
	fp[4] = 0x40;
	fp[5] = 0x00;
	fp[6] = 0x0F;
	fp[7] = 0x00;
	fp[8] = 1;		/* Length: 1 * 16 = 16 bytes */
	fp[9] = 4;		/* MP spec version 1.4 */
	fp[10] = 0;		/* Checksum - fill later */
	fp[11] = 0;		/* Feature 1: 0 means config table is present */
	fp[12] = 0;		/* Feature 2: bit 7 = IMCR present */
	fp[13] = 0;
	fp[14] = 0;
	fp[15] = 0;
	fp[10] = -mpchecksum(fp, 16);
	
	/*
	 * MP Configuration Table Header (44 bytes)
	 */
	p = cfg;
	
	/* Signature */
	p[0] = 'P';
	p[1] = 'C';
	p[2] = 'M';
	p[3] = 'P';
	/* Skip length (offset 4-5), fill later */
	/* Spec revision */
	p[6] = 4;
	/* Skip checksum (offset 7), fill later */
	/* OEM ID (8 bytes) */
	memcpy(p + 8, "9FRONTVX", 8);
	/* Product ID (12 bytes) */
	memcpy(p + 16, "VMXSMP      ", 12);
	/* OEM Table Pointer (4 bytes) = 0 */
	p[28] = 0; p[29] = 0; p[30] = 0; p[31] = 0;
	/* OEM Table Size (2 bytes) = 0 */
	p[32] = 0; p[33] = 0;
	/* Entry Count - fill later at offset 34 */
	/* Local APIC Address (4 bytes) = 0xFEE00000 little-endian */
	p[36] = 0x00;
	p[37] = 0x00;
	p[38] = 0xE0;
	p[39] = 0xFE;
	/* Extended Table Length (2 bytes) = 0 */
	p[40] = 0; p[41] = 0;
	/* Extended Table Checksum = 0 */
	p[42] = 0;
	/* Reserved */
	p[43] = 0;
	
	/* Entries start at offset 44 */
	entrystart = cfg + 44;
	p = entrystart;
	entries = 0;
	
	/*
	 * Processor entries (20 bytes each)
	 */
	for(i = 0; i < ncpu; i++){
		p[0] = MP_PROCESSOR;
		p[1] = i;			/* APIC ID */
		p[2] = 0x14;			/* APIC version */
		p[3] = 0x01 | (i == 0 ? 0x02 : 0x00);  /* enabled + BSP for CPU 0 */
		/* CPU signature: family 6, model 9, stepping 1 */
		p[4] = 0x91;  /* stepping=1, model low=9 */
		p[5] = 0x06;  /* family low=6, model high=0 */
		p[6] = 0x00;  /* family high=0, type=0 */
		p[7] = 0x00;
		/* Feature flags - basic x86 features */
		p[8] = 0x78;   /* FPU, VME, DE, PSE */
		p[9] = 0x1A;   /* TSC, MSR, PAE, MCE */
		p[10] = 0x20;  /* APIC */
		p[11] = 0x00;
		p[12] = 0x00;
		p[13] = 0x00;
		p[14] = 0x00;
		p[15] = 0x00;
		/* Reserved */
		p[16] = 0; p[17] = 0; p[18] = 0; p[19] = 0;
		p += 20;
		entries++;
	}
	
	/*
	 * Bus entries (8 bytes each)
	 * 
	 * IMPORTANT: The kernel looks up buses by TYPE and NUMBER.
	 * PCI devices request "BusPCI, number 0" so we MUST have PCI as bus 0.
	 * ISA devices use mpisabus which gets set from parsing these entries.
	 */
	
	/* PCI bus - ID 0 (primary PCI bus) */
	p[0] = MP_BUS;
	p[1] = BUS_PCI;			/* Bus ID 0 */
	memcpy(p + 2, "PCI   ", 6);
	p += 8;
	entries++;
	
	/* ISA bus - ID 1 (behind PCI-ISA bridge) */
	p[0] = MP_BUS;
	p[1] = BUS_ISA;			/* Bus ID 1 */
	memcpy(p + 2, "ISA   ", 6);
	p += 8;
	entries++;
	
	/*
	 * I/O APIC entry (8 bytes)
	 */
	p[0] = MP_IOAPIC;
	p[1] = ncpu;		/* I/O APIC ID = ncpu (after CPU IDs) */
	p[2] = 0x11;		/* Version */
	p[3] = 0x01;		/* Enabled */
	/* Address 0xFEC00000 little-endian */
	p[4] = 0x00;
	p[5] = 0x00;
	p[6] = 0xC0;
	p[7] = 0xFE;
	p += 8;
	entries++;
	
	/*
	 * I/O Interrupt entries (8 bytes each)
	 * 
	 * Map ISA IRQs to I/O APIC pins
	 * IRQ 0 (timer) -> pin 2 (standard override)
	 * IRQ 2 is not used (was cascade)
	 * All other IRQs -> identity mapping
	 */
	
	/* IRQ 0 -> IOAPIC pin 2 (timer override) */
	p[0] = MP_IOINTR;
	p[1] = 0;           /* INT type */
	p[2] = 0;           /* Polarity: conforms to bus spec */
	p[3] = 0;           /* Trigger: conforms (edge for ISA) */
	p[4] = BUS_ISA;     /* Source: ISA bus */
	p[5] = 0;           /* Source IRQ 0 */
	p[6] = ncpu;        /* Dest: I/O APIC ID */
	p[7] = 2;           /* INTIN# 2 */
	p += 8;
	entries++;
	
	/* IRQs 1, 3-15 -> identity mapping (skip IRQ 2, it's cascade) */
	for(i = 1; i < 16; i++){
	    if(i == 2) continue;  /* Skip cascade */
	    p[0] = MP_IOINTR;
	    p[1] = 0;
	    p[2] = 0;
	    p[3] = 0;
	    p[4] = BUS_ISA;
	    p[5] = i;       /* Source IRQ */
	    p[6] = ncpu;    /* Dest: I/O APIC ID */
	    p[7] = i;       /* INTIN# = IRQ# */
	    p += 8;
	    entries++;
}

	/*
	 * PCI interrupt routing entries
	 * Map PCI devices to I/O APIC pins 16-19
	 * Source bus is PCI (BUS_PCI = 0)
	 * 
	 * The IRQ field for PCI encodes (device << 2) | (pin - 1)
	 * where pin is 1=INTA, 2=INTB, 3=INTC, 4=INTD
	 * 
	 * pcibusmap assigns: irqno = 16 + (devno % 4)
	 * So device 1 -> pin 17, device 2 -> pin 18, etc.
	 * 
	 * We create entries for devices 1-4 (device 0 is host bridge)
	 */
	for(i = 1; i <= 4; i++){
	    p[0] = MP_IOINTR;
	    p[1] = 0;           /* INT type */
	    p[2] = 0x0F;        /* Flags: active-low (bits 1:0=11) + level (bits 3:2=11) = 0x0F */
	    p[3] = 0x00;        /* Flags high byte = 0 */
	    p[4] = BUS_PCI;     /* Source: PCI bus (ID 0) */
	    p[5] = (i << 2) | 0;    /* device i, INTA */
	    p[6] = ncpu;        /* Dest: I/O APIC ID */
	    p[7] = 16 + (i % 4);    /* INTIN# */
	    p += 8;
	    entries++;
	}
 
	/*
	 * Local Interrupt entries (8 bytes each)
	 */
	
	/* LINT0: ExtINT (for 8259 compatibility) */
	p[0] = MP_LINTR;
	p[1] = 3;		/* ExtINT */
	p[2] = 0;		/* Polarity: conforms */
	p[3] = 0;		/* Trigger: conforms */
	p[4] = BUS_ISA;		/* Source bus: ISA */
	p[5] = 0;		/* Source IRQ */
	p[6] = 0xFF;		/* Dest: all local APICs */
	p[7] = 0;		/* LINT0 */
	p += 8;
	entries++;
	
	/* LINT1: NMI */
	p[0] = MP_LINTR;
	p[1] = 1;		/* NMI */
	p[2] = 0;		/* Polarity: conforms */
	p[3] = 0;		/* Trigger: conforms */
	p[4] = BUS_ISA;		/* Source bus: ISA */
	p[5] = 0;		/* Source IRQ */
	p[6] = 0xFF;		/* All local APICs */
	p[7] = 1;		/* LINT1 */
	p += 8;
	entries++;
	
	/*
	 * Fill in table length and entry count
	 */
	tbllen = p - cfg;
	cfg[4] = tbllen & 0xFF;
	cfg[5] = (tbllen >> 8) & 0xFF;
	cfg[34] = entries & 0xFF;
	cfg[35] = (entries >> 8) & 0xFF;
	
	/* Compute checksum */
	cfg[7] = -mpchecksum(cfg, tbllen);
	
	/* Debug: print what we created */
	dprint("mpmktable: created MP table at 0xF0000");
	dprint("  %d CPUs, %d entries, %d bytes", ncpu, entries, tbllen);
	dprint("  PCI bus ID=%d, ISA bus ID=%d", BUS_PCI, BUS_ISA);
}