shithub: vmxsmp

ref: e0e889fb69a6d4d3f2332244ef4f79f900a66d84
dir: /acpi.c/

View raw version
/*
 * 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);
}