ref: e0e889fb69a6d4d3f2332244ef4f79f900a66d84
dir: /mptable.c/
/*
* 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);
}