ref: e0e889fb69a6d4d3f2332244ef4f79f900a66d84
dir: /acpi.c/
/*
* acpi.c - ACPI Table Generation for vmx
*
* This generates the minimum ACPI tables needed for SMP and I/O APIC:
* - RSDP (Root System Description Pointer) - at 0xE0000
* - RSDT (Root System Description Table)
* - MADT (Multiple APIC Description Table) - describes APICs
* - FADT (Fixed ACPI Description Table) - basic system info
* - DSDT (Differentiated System Description Table) - minimal
*
* Memory layout (in guest physical memory):
* 0x000E0000 - RSDP (36 bytes for ACPI 2.0)
* 0x000E1000 - RSDT
* 0x000E1100 - MADT
* 0x000E1300 - FADT
* 0x000E1400 - FACS
* 0x000E1500 - DSDT
*/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "dat.h"
#include "fns.h"
/* ACPI table signatures */
#define RSDP_SIG "RSD PTR "
#define RSDT_SIG "RSDT"
#define MADT_SIG "APIC"
#define FADT_SIG "FACP"
#define FACS_SIG "FACS"
#define DSDT_SIG "DSDT"
#define HPET_SIG "HPET"
/* Physical addresses for tables */
#define RSDP_ADDR 0x000E0000
#define RSDT_ADDR 0x000E1000
#define MADT_ADDR 0x000E1100
#define FADT_ADDR 0x000E1300
#define FACS_ADDR 0x000E1400
#define DSDT_ADDR 0x000E1500
#define HPET_ADDR 0x000E1800
/* MADT entry types */
enum {
MADT_LAPIC = 0, /* Processor Local APIC */
MADT_IOAPIC = 1, /* I/O APIC */
MADT_ISO = 2, /* Interrupt Source Override */
MADT_NMI = 4, /* Local APIC NMI */
};
/* Compute ACPI checksum - sum of all bytes must be 0 */
u8int
acpi_checksum(void *data, int len)
{
u8int sum = 0;
u8int *p = data;
while(len-- > 0)
sum += *p++;
return -sum;
}
/* Write a 32-bit value in little-endian */
static void
put32(u8int *p, u32int v)
{
p[0] = v;
p[1] = v >> 8;
p[2] = v >> 16;
p[3] = v >> 24;
}
/* Write a 16-bit value in little-endian */
static void
put16(u8int *p, u16int v)
{
p[0] = v;
p[1] = v >> 8;
}
/*
* Build RSDP (Root System Description Pointer)
* This is what the OS searches for to find ACPI tables.
* Located in BIOS area (0xE0000-0xFFFFF) on 16-byte boundary.
*
* ACPI 1.0 RSDP: 20 bytes
* ACPI 2.0 RSDP: 36 bytes (extended)
*/
static int
build_rsdp(u8int *p)
{
memset(p, 0, 36);
/* Signature "RSD PTR " (8 bytes, note trailing space) */
memcpy(p + 0, RSDP_SIG, 8);
/* Checksum (offset 8) - filled later */
/* OEM ID (6 bytes) */
memcpy(p + 9, "9FRONT", 6);
/* Revision: 0 = ACPI 1.0, 2 = ACPI 2.0+ */
p[15] = 0; /* Use ACPI 1.0 for simplicity */
/* RSDT Address (4 bytes) */
put32(p + 16, RSDT_ADDR);
/* For ACPI 1.0, structure ends here (20 bytes) */
/* Checksum covers bytes 0-19 */
p[8] = acpi_checksum(p, 20);
return 20;
}
/*
* Build standard ACPI table header
* Used by RSDT, MADT, FADT, DSDT
*/
static void
build_header(u8int *p, char *sig, u32int len, u8int rev)
{
/* Signature (4 bytes) */
memcpy(p + 0, sig, 4);
/* Length (4 bytes) */
put32(p + 4, len);
/* Revision */
p[8] = rev;
/* Checksum (offset 9) - filled by caller */
p[9] = 0;
/* OEM ID (6 bytes) */
memcpy(p + 10, "9FRONT", 6);
/* OEM Table ID (8 bytes) */
memcpy(p + 16, "VMXACPI ", 8);
/* OEM Revision */
put32(p + 24, 1);
/* Creator ID */
memcpy(p + 28, "VMX ", 4);
/* Creator Revision */
put32(p + 32, 1);
}
/*
* Build RSDT (Root System Description Table)
* Contains pointers to other ACPI tables.
*/
static int
build_rsdt(u8int *p)
{
int len;
/* Header (36 bytes) + 3 table pointers (12 bytes) */
len = 36 + 12;
memset(p, 0, len);
build_header(p, RSDT_SIG, len, 1);
/* Table pointers start at offset 36 */
put32(p + 36, MADT_ADDR); /* Pointer to MADT */
put32(p + 40, FADT_ADDR); /* Pointer to FADT */
put32(p + 44, HPET_ADDR); /* Pointer to HPET */
/* Checksum */
p[9] = acpi_checksum(p, len);
return len;
}
/*
* Build MADT (Multiple APIC Description Table)
* This is the key table for I/O APIC and Local APIC configuration.
*/
static int
build_madt(u8int *p, int ncpu)
{
u8int *start = p;
int i, len;
/* We'll fill in length later */
memset(p, 0, 512); /* Reserve space */
/* Skip header for now (36 bytes) */
p += 36;
/* Local APIC Address (4 bytes) */
put32(p, 0xFEE00000);
p += 4;
/* Flags (4 bytes) - bit 0 = PCAT_COMPAT (dual 8259 present) */
put32(p, 1);
p += 4;
/* === MADT Entries === */
/* Local APIC entries - one per CPU */
for(i = 0; i < ncpu; i++){
p[0] = MADT_LAPIC; /* Type */
p[1] = 8; /* Length */
p[2] = i; /* ACPI Processor ID */
p[3] = i; /* APIC ID */
put32(p + 4, 1); /* Flags: enabled */
p += 8;
}
/* I/O APIC entry */
p[0] = MADT_IOAPIC; /* Type */
p[1] = 12; /* Length */
p[2] = ncpu; /* I/O APIC ID (after CPU IDs) */
p[3] = 0; /* Reserved */
put32(p + 4, 0xFEC00000); /* I/O APIC Address */
put32(p + 8, 0); /* Global System Interrupt Base */
p += 12;
/*
* Interrupt Source Override entries
* These remap ISA IRQs that differ from their APIC pin numbers.
* Standard ISA has IRQ 0 = timer, but APIC usually maps it to pin 2.
*/
/* IRQ 0 (timer) -> GSI 2 */
p[0] = MADT_ISO; /* Type */
p[1] = 10; /* Length */
p[2] = 0; /* Bus (0 = ISA) */
p[3] = 0; /* Source (IRQ 0) */
put32(p + 4, 2); /* Global System Interrupt (pin 2) */
put16(p + 8, 0); /* Flags: conforming polarity/trigger */
p += 10;
/* IRQ 9 -> GSI 9 (ACPI SCI, level triggered) */
p[0] = MADT_ISO;
p[1] = 10;
p[2] = 0; /* Bus (ISA) */
p[3] = 9; /* Source (IRQ 9) */
put32(p + 4, 9); /* GSI 9 */
put16(p + 8, 0x000D); /* Flags: active low, level triggered */
p += 10;
/* Local APIC NMI - connect LINT1 to NMI for all processors */
for(i = 0; i < ncpu; i++){
p[0] = MADT_NMI; /* Type */
p[1] = 6; /* Length */
p[2] = i; /* ACPI Processor ID (or 0xFF for all) */
put16(p + 3, 0x0005); /* Flags: active high, edge triggered */
p[5] = 1; /* Local APIC LINT# (LINT1 = NMI) */
p += 6;
}
/* Calculate total length */
len = p - start;
/* Now fill in header */
build_header(start, MADT_SIG, len, 3); /* MADT revision 3 */
/* Checksum */
start[9] = acpi_checksum(start, len);
return len;
}
/*
* Build FACS (Firmware ACPI Control Structure)
* Minimal structure for basic ACPI support.
*/
static int
build_facs(u8int *p)
{
memset(p, 0, 64);
/* Signature */
memcpy(p + 0, FACS_SIG, 4);
/* Length */
put32(p + 4, 64);
/* Hardware Signature */
put32(p + 8, 0);
/* Firmware Waking Vector */
put32(p + 12, 0);
/* Global Lock */
put32(p + 16, 0);
/* Flags */
put32(p + 20, 0);
/* Version */
p[32] = 1;
return 64;
}
static int
build_dsdt(u8int *p)
{
/*
* DSDT with _CRS and _PRT for PCI, plus PS/2 keyboard and mouse.
*/
static u8int aml[] = {
/* Scope(\_SB_) - updated PkgLen for added devices */
0x10, 0x4B, 0x0F, /* ScopeOp, 2-byte PkgLen=251 */
0x5C, 0x5F, 0x53, 0x42, 0x5F, /* "\_SB_" */
/* Device(PCI0) - unchanged */
0x5B, 0x82, 0x44, 0x0A, /* DeviceOp, 2-byte PkgLen=164 */
0x50, 0x43, 0x49, 0x30, /* "PCI0" */
/* Name(_HID, EisaId("PNP0A03")) - 10 bytes */
0x08,
0x5F, 0x48, 0x49, 0x44,
0x0C, 0x41, 0xD0, 0x0A, 0x03,
/* Name(_CRS, Buffer {...}) - resource template */
0x08, /* NameOp */
0x5F, 0x43, 0x52, 0x53, /* "_CRS" */
0x11, 0x25, /* BufferOp, PkgLen=37 */
0x0A, 0x22, /* ByteConst 34 = buffer size */
/* WordBusNumber - 16 bytes */
0x88, 0x0D, 0x00, /* Word descriptor, len=13 */
0x02, /* Bus number type */
0x0C, /* Flags */
0x00, /* Type flags */
0x00, 0x00, /* Granularity */
0x00, 0x00, /* Min: 0 */
0xFF, 0x00, /* Max: 255 */
0x00, 0x00, /* Translation */
0x00, 0x01, /* Length: 256 */
/* WordIO - 16 bytes */
0x88, 0x0D, 0x00, /* Word descriptor, len=13 */
0x01, /* I/O type */
0x0C, /* Flags */
0x03, /* Type flags */
0x00, 0x00, /* Granularity */
0x00, 0x10, /* Min: 0x1000 */
0xFF, 0xFF, /* Max: 0xFFFF */
0x00, 0x00, /* Translation */
0x00, 0xF0, /* Length: 0xF000 */
/* End tag - 2 bytes */
0x79, 0x00,
/* Name(_PRT, Package(8) {...}) */
0x08, /* NameOp */
0x5F, 0x50, 0x52, 0x54, /* "_PRT" */
0x12, 0x43, 0x06, /* PackageOp, 2-byte PkgLen=99 */
0x08, /* 8 elements */
/* dev 1 -> GSI 17 */
0x12, 0x0B, 0x04, 0x0C, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x11,
/* dev 2 -> GSI 18 */
0x12, 0x0B, 0x04, 0x0C, 0xFF, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0A, 0x12,
/* dev 3 -> GSI 19 */
0x12, 0x0B, 0x04, 0x0C, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0A, 0x13,
/* dev 4 -> GSI 16 */
0x12, 0x0B, 0x04, 0x0C, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x0A, 0x10,
/* dev 5 -> GSI 17 */
0x12, 0x0B, 0x04, 0x0C, 0xFF, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x0A, 0x11,
/* dev 6 -> GSI 18 */
0x12, 0x0B, 0x04, 0x0C, 0xFF, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x0A, 0x12,
/* dev 7 -> GSI 19 */
0x12, 0x0B, 0x04, 0x0C, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x0A, 0x13,
/* dev 8 -> GSI 16 */
0x12, 0x0B, 0x04, 0x0C, 0xFF, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x0A, 0x10,
/* ============================================================
* Device(PS2K) - PS/2 Keyboard Controller
* ============================================================ */
0x5B, 0x82, 0x2D, /* DeviceOp, PkgLen=45 */
0x50, 0x53, 0x32, 0x4B, /* "PS2K" */
/* Name(_HID, EisaId("PNP0303")) - IBM Enhanced Keyboard */
0x08, /* NameOp */
0x5F, 0x48, 0x49, 0x44, /* "_HID" */
0x0C, 0x41, 0xD0, 0x03, 0x03, /* DWordConst PNP0303 */
/* Name(_CRS, ResourceTemplate() { IO, IO, IRQ }) */
0x08, /* NameOp */
0x5F, 0x43, 0x52, 0x53, /* "_CRS" */
0x11, 0x18, /* BufferOp, PkgLen=24 */
0x0A, 0x15, /* ByteConst 21 = buffer size */
/* IO(Decode16, 0x60, 0x60, 0, 1) - data port */
0x47, 0x01, 0x60, 0x00, 0x60, 0x00, 0x00, 0x01,
/* IO(Decode16, 0x64, 0x64, 0, 1) - command port */
0x47, 0x01, 0x64, 0x00, 0x64, 0x00, 0x00, 0x01,
/* IRQNoFlags() {1} */
0x22, 0x02, 0x00,
/* End tag */
0x79, 0x00,
/* ============================================================
* Device(PS2M) - PS/2 Mouse
* ============================================================ */
0x5B, 0x82, 0x1D, /* DeviceOp, PkgLen=29 */
0x50, 0x53, 0x32, 0x4D, /* "PS2M" */
/* Name(_HID, EisaId("PNP0F13")) - PS/2 Mouse */
0x08, /* NameOp */
0x5F, 0x48, 0x49, 0x44, /* "_HID" */
0x0C, 0x41, 0xD0, 0x0F, 0x13, /* DWordConst PNP0F13 */
/* Name(_CRS, ResourceTemplate() { IRQ }) */
0x08, /* NameOp */
0x5F, 0x43, 0x52, 0x53, /* "_CRS" */
0x11, 0x08, /* BufferOp, PkgLen=8 */
0x0A, 0x05, /* ByteConst 5 = buffer size */
/* IRQNoFlags() {12} */
0x22, 0x00, 0x10,
/* End tag */
0x79, 0x00,
};
int aml_len = sizeof(aml);
int len = 36 + aml_len;
memset(p, 0, len);
build_header(p, DSDT_SIG, len, 1);
memcpy(p + 36, aml, aml_len);
p[9] = acpi_checksum(p, len);
dprint("build_dsdt: aml_len=%d total=%d\n", aml_len, len);
return len;
}
/*
* Build FADT (Fixed ACPI Description Table)
* Describes fixed ACPI features and points to FACS/DSDT.
*/
static int
build_fadt(u8int *p)
{
int len;
/* FADT is 116 bytes for ACPI 1.0, 244 for ACPI 2.0+ */
/* Use ACPI 1.0 size for simplicity */
len = 116;
memset(p, 0, len);
build_header(p, FADT_SIG, len, 1); /* FADT revision 1 */
/* FACS Address (4 bytes at offset 36) */
put32(p + 36, FACS_ADDR);
/* DSDT Address (4 bytes at offset 40) */
put32(p + 40, DSDT_ADDR);
/* Interrupt Model (offset 44): 0 = dual PIC, 1 = APIC */
p[44] = 1; /* APIC mode */
/* Preferred PM Profile (offset 45): 0 = unspecified */
p[45] = 0;
/* SCI Interrupt (offset 46): IRQ 9 */
put16(p + 46, 9);
/* SMI Command Port (offset 48): 0 = no SMI */
put32(p + 48, 0);
/* ACPI Enable/Disable values (offsets 52, 53) */
p[52] = 0; /* ACPI_ENABLE */
p[53] = 0; /* ACPI_DISABLE */
/* S4BIOS_REQ (offset 54) */
p[54] = 0;
/* PSTATE_CNT (offset 55) */
p[55] = 0;
/* PM1a Event Block (4 bytes at offset 56) - required */
put32(p + 56, 0x400); /* PM1a_EVT_BLK - use port 0x400 */
/* PM1b Event Block (offset 60): not used */
put32(p + 60, 0);
/* PM1a Control Block (4 bytes at offset 64) - required */
put32(p + 64, 0x404); /* PM1a_CNT_BLK - use port 0x404 */
/* PM1b Control Block (offset 68): not used */
put32(p + 68, 0);
/* PM2 Control Block (offset 72): not used */
put32(p + 72, 0);
/* PM Timer Block (offset 76): not used */
put32(p + 76, 0x408);
/* GPE0 Block (offset 80): not used */
put32(p + 80, 0);
/* GPE1 Block (offset 84): not used */
put32(p + 84, 0);
/* PM1a Event Block Length (offset 88) */
p[88] = 4; /* PM1_EVT_LEN */
/* PM1a Control Block Length (offset 89) */
p[89] = 2; /* PM1_CNT_LEN */
/* PM2 Control Length (offset 90) */
p[90] = 0;
/* PM Timer Length (offset 91) */
p[91] = 4;
/* GPE0 Block Length (offset 92) */
p[92] = 0;
/* GPE1 Block Length (offset 93) */
p[93] = 0;
/* GPE1 Base (offset 94) */
p[94] = 0;
/* CST_CNT (offset 95) */
p[95] = 0;
/* P_LVL2_LAT (offset 96) */
put16(p + 96, 0xFFFF); /* C2 not supported */
/* P_LVL3_LAT (offset 98) */
put16(p + 98, 0xFFFF); /* C3 not supported */
/* FLUSH_SIZE (offset 100) */
put16(p + 100, 0);
/* FLUSH_STRIDE (offset 102) */
put16(p + 102, 0);
/* DUTY_OFFSET (offset 104) */
p[104] = 0;
/* DUTY_WIDTH (offset 105) */
p[105] = 0;
/* Day Alarm (offset 106) */
p[106] = 0;
/* Month Alarm (offset 107) */
p[107] = 0;
/* Century (offset 108) */
p[108] = 0;
/* IAPC_BOOT_ARCH (offset 109-110): legacy devices present */
put16(p + 109, 0x0003); /* 8042, VGA present */
/* Reserved (offset 111) */
p[111] = 0;
/* Flags (offset 112) */
put32(p + 112,
(1 << 0) | /* WBINVD supported */
(1 << 4) | /* Power button is control method */
(1 << 5) | /* Sleep button is control method */
(1 << 8) | /* TMR_VAL_EXT: 32-bit PM timer */
(1 << 10)); /* Headless system (no VGA) - set to 0 if VGA present */
/* Checksum */
p[9] = acpi_checksum(p, len);
return len;
}
/*
* Build HPET (High Precision Event Timer) Table
* Tells the OS where the HPET is located.
*
* Structure (ACPI spec):
* 0-35: Standard header (36 bytes)
* 36-39: Event Timer Block ID (4 bytes)
* 40-51: Base Address - Generic Address Structure (12 bytes)
* 52: HPET Sequence Number (1 byte)
* 53-54: Minimum Clock Tick (2 bytes)
* 55: Page Protection (1 byte)
* Total: 56 bytes
*/
static int
build_hpet(u8int *p)
{
int len;
/* HPET table is 56 bytes */
len = 56;
memset(p, 0, len);
build_header(p, HPET_SIG, len, 1);
/*
* Event Timer Block ID (offset 36, 4 bytes)
* Bits 0-7: Hardware Rev ID
* Bits 8-12: Number of comparators - 1
* Bit 13: COUNT_SIZE_CAP (1 = 64-bit)
* Bit 14: Reserved
* Bit 15: Legacy Replacement Route Capable
* Bits 16-31: PCI Vendor ID
*/
put32(p + 36,
(0x01) | /* Hardware Rev ID = 1 */
(2 << 8) | /* 3 comparators (n-1=2) */
(1 << 13) | /* 64-bit counter */
(1 << 15) | /* Legacy route capable */
(0x8086 << 16)); /* Intel vendor ID */
/*
* Base Address - Generic Address Structure (offset 40, 12 bytes)
* This is a GAS (Generic Address Structure):
* Byte 0: Address Space ID (0 = System Memory)
* Byte 1: Register Bit Width
* Byte 2: Register Bit Offset
* Byte 3: Access Size (0=undefined, 1=byte, 2=word, 3=dword, 4=qword)
* Bytes 4-11: Address (64-bit)
*/
p[40] = 0; /* Address Space ID = System Memory */
p[41] = 64; /* Register Bit Width = 64 */
p[42] = 0; /* Register Bit Offset = 0 */
p[43] = 0; /* Access Size = undefined (let OS decide) */
put32(p + 44, 0xFED00000); /* Address low 32 bits */
put32(p + 48, 0); /* Address high 32 bits */
/* HPET Sequence Number (offset 52, 1 byte) */
p[52] = 0;
/* Minimum Clock Tick (offset 53, 2 bytes) */
put16(p + 53, 0); /* 0 = no minimum */
/* Page Protection (offset 55, 1 byte) */
p[55] = 0; /* No page protection */
/* Checksum */
p[9] = acpi_checksum(p, len);
return len;
}
/*
* Generate all ACPI tables
* Call this from vmxsetup() or similar initialization code.
*/
void
acpimktables(void)
{
u8int *rsdp, *rsdt, *madt, *fadt, *facs, *dsdt, *hpet;
int rsdp_len, rsdt_len, madt_len, fadt_len, facs_len, dsdt_len, hpet_len;
/* Get pointers to guest memory */
rsdp = gptr(RSDP_ADDR, 0x100);
rsdt = gptr(RSDT_ADDR, 0x100);
madt = gptr(MADT_ADDR, 0x200);
fadt = gptr(FADT_ADDR, 0x100);
facs = gptr(FACS_ADDR, 0x100);
dsdt = gptr(DSDT_ADDR, 0x300);
hpet = gptr(HPET_ADDR, 0x100);
if(rsdp == nil || rsdt == nil || madt == nil ||
fadt == nil || facs == nil || dsdt == nil || hpet == nil){
vmerror("acpimktables: cannot map ACPI table memory");
return;
}
/* Build tables */
facs_len = build_facs(facs);
dsdt_len = build_dsdt(dsdt);
fadt_len = build_fadt(fadt);
madt_len = build_madt(madt, nvcpu);
hpet_len = build_hpet(hpet);
rsdt_len = build_rsdt(rsdt);
rsdp_len = build_rsdp(rsdp);
dprint("ACPI tables created:");
dprint(" RSDP at %#x (%d bytes)", RSDP_ADDR, rsdp_len);
dprint(" RSDT at %#x (%d bytes)", RSDT_ADDR, rsdt_len);
dprint(" MADT at %#x (%d bytes) - %d CPUs", MADT_ADDR, madt_len, nvcpu);
dprint(" FADT at %#x (%d bytes)", FADT_ADDR, fadt_len);
dprint(" FACS at %#x (%d bytes)", FACS_ADDR, facs_len);
dprint(" DSDT at %#x (%d bytes)", DSDT_ADDR, dsdt_len);
dprint(" HPET at %#x (%d bytes)", HPET_ADDR, hpet_len);
}