// hal_main.c - HAL initialisation and HalGetApi #include #include #include #include #include #include // Declared in other hal_*.c files: void HalScreenInitialize(uintptr_t fb, int w, int h, int stride, HAL_SCREEN* out); void HalKeyboardInitialize(HAL_KEYBOARD* out); KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *out); void HalPciInitialize(HAL_PCI* out); void HalDiskInitialize(HAL_DISK* out); extern void HalSetDebugEnabled(int v); // Defined in mm_virt.c - HAL needs it for MMIO window access. static uint64_t gPhysBase = 0xFFFF880000000000ULL; HAL_API gApi; static int gInitialised = 0; extern void (*gTimerCallback)(void); // --------------------------------------------------------------------------- // ACPI table walker - minimal, just enough to find APIC and HPET addresses. // --------------------------------------------------------------------------- #define ACPI_SIG(a,b,c,d) ((uint32_t)(a)|((uint32_t)(b)<<8)|((uint32_t)(c)<<16)|((uint32_t)(d)<<24)) #pragma pack(push, 1) #pragma warning(disable : 4200) // ACPI RSDP (Root System Description Pointer) typedef struct { char Signature[8]; uint8_t Checksum; char OEMID[6]; uint8_t Revision; uint32_t RsdtAddress; uint32_t Length; uint64_t XsdtAddress; uint8_t ExtendedChecksum; uint8_t Reserved[3]; } RSDP; // ACPI SDT Header typedef struct { char Signature[4]; uint32_t Length; uint8_t Revision; uint8_t Checksum; char OEMID[6]; char OEMTableID[8]; uint32_t OEMRevision; uint32_t CreatorID; uint32_t CreatorRevision; } ACPISDTHeader; // RSDT (Root System Description Table) typedef struct { ACPISDTHeader Header; uint32_t PointerToOtherSDT[]; } RSDT; // XSDT (Extended System Description Table) typedef struct { ACPISDTHeader Header; uint64_t PointerToOtherSDT[]; } XSDT; // MADT (Multiple APIC Description Table) typedef struct { ACPISDTHeader Header; uint32_t LocalAPICAddress; uint32_t Flags; uint8_t Entries[]; } MADT; #pragma pack(pop) static uintptr_t gLapicPhys = 0xFEE00000; static uintptr_t gIoapicPhys = 0xFEC00000; static uintptr_t gHpetPhys = 0xFED00000; // ACPI power-off state (filled in by ParseFadt / ScanDsdtForS5) static uint16_t gPm1aCntBlk = 0; static uint16_t gPm1bCntBlk = 0; static uint8_t gSlpTypA = 5; // S5 SLP_TYP for PM1a; 5 is correct for QEMU/most VMs static uint8_t gSlpTypB = 5; // S5 SLP_TYP for PM1b int debugEnabled = 0; int _fltused; // fuckass static void hal_putchar(char c) { gApi.Screen.PrintChar(c, 0x00FF00, 0x000000); } void HalDbgPrint(const char* fmt, ...) { if (!debugEnabled) return; va_list args; va_start(args, fmt); vprintf(fmt, args); va_end(args); } static void *HalPhysToVirt(uint64_t phys) { return (void *)(phys + gPhysBase); } // FADT (Fixed ACPI Description Table) - partial layout up to PM1b_CNT_BLK. // We only need offsets 40 (DSDT ptr), 64 (PM1a_CNT_BLK), 68 (PM1b_CNT_BLK). // The full table is ~276 bytes (ACPI 2.0+); we never access beyond offset 68. #pragma pack(push, 1) typedef struct { ACPISDTHeader Header; // 0 (36 bytes) uint32_t FirmwareCtrl; // 36 uint32_t Dsdt; // 40 - physical addr of DSDT uint8_t Reserved1; // 44 uint8_t PreferredPmProfile; // 45 uint16_t SciInt; // 46 uint32_t SmiCmd; // 48 uint8_t AcpiEnable; // 52 uint8_t AcpiDisable; // 53 uint8_t S4BiosReq; // 54 uint8_t PstateCnt; // 55 uint32_t Pm1aEvtBlk; // 56 uint32_t Pm1bEvtBlk; // 60 uint32_t Pm1aCntBlk; // 64 - I/O port for PM1a control register uint32_t Pm1bCntBlk; // 68 - I/O port for PM1b control register } FADT; #pragma pack(pop) // Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values. // The encoding looks like: // 0x08 '_' 'S' '5' '_' ; NameOp + "_S5_" // 0x12 ; PackageOp + encoded length // ; usually 0x04 // 0x0A ; BytePrefix + value (or 0x00 for zero) // 0x0A static void ScanDsdtForS5(const uint8_t* aml, uint32_t len) { for (uint32_t i = 0; i + 9 < len; i++) { // Match NameOp + "_S5_" + PackageOp if (aml[i] != 0x08) continue; if (aml[i+1] != 0x5F || aml[i+2] != 0x53 || aml[i+3] != 0x35 || aml[i+4] != 0x5F) continue; if (aml[i+5] != 0x12) continue; // PkgLength encoding: low 6 bits = byte count (0 → only this byte); // bits [7:6] = number of following bytes in the length field. uint8_t pkgByte = aml[i+6]; uint32_t pkgExtra = pkgByte >> 6; // 0..3 extra bytes uint32_t j = i + 7 + pkgExtra; // skip PkgLen field if (j + 2 >= len) break; j++; // skip NumElements byte // SLP_TYP_A if (aml[j] == 0x0A && j + 1 < len) { gSlpTypA = aml[j + 1]; j += 2; } else if (aml[j] == 0x00) { gSlpTypA = 0; j++; } else { break; // unexpected encoding - keep defaults } // SLP_TYP_B if (j < len && aml[j] == 0x0A && j + 1 < len) gSlpTypB = aml[j + 1]; else if (j < len && aml[j] == 0x00) gSlpTypB = 0; HalDbgPrint("HAL: _S5_ SLP_TYP_A=0x%02x SLP_TYP_B=0x%02x\n", gSlpTypA, gSlpTypB); return; } HalDbgPrint("HAL: _S5_ not found in DSDT, using defaults (A=0x%02x B=0x%02x)\n", gSlpTypA, gSlpTypB); } static int validate_checksum(void* table, uint32_t length) { uint8_t sum = 0; uint8_t* bytes = (uint8_t*)table; for (uint32_t i = 0; i < length; i++) { sum += bytes[i]; } return sum == 0; } static void ParseFadt(const FADT* fadt) { gPm1aCntBlk = (uint16_t)fadt->Pm1aCntBlk; gPm1bCntBlk = (uint16_t)fadt->Pm1bCntBlk; HalDbgPrint("HAL: FADT PM1a_CNT=0x%04x PM1b_CNT=0x%04x DSDT=0x%08x\n", gPm1aCntBlk, gPm1bCntBlk, fadt->Dsdt); if (fadt->Dsdt) { const ACPISDTHeader* dsdt = (const ACPISDTHeader*)((uintptr_t)fadt->Dsdt + gPhysBase); if (memcmp(dsdt->Signature, "DSDT", 4) == 0 && validate_checksum((void*)dsdt, dsdt->Length)) { const uint8_t* aml = (const uint8_t*)(dsdt + 1); ScanDsdtForS5(aml, dsdt->Length - (uint32_t)sizeof(ACPISDTHeader)); } } } static void ParseMadt(MADT* madt) { gLapicPhys = madt->LocalAPICAddress; uint8_t* p = (uint8_t *)(madt + 1); uint8_t* end = (uint8_t *)madt + madt->Header.Length; while (p < end) { uint8_t type = p[0], len = p[1]; if (type == 1) { // I/O APIC entry gIoapicPhys = *(uint32_t *)(p + 4); } p += len ? len : 1; } } static void WalkAcpi(uintptr_t rsdpPhys) { if (!rsdpPhys) return; // Map RSDP into virtual memory RSDP* rsdp = (RSDP*)(rsdpPhys + gPhysBase); if (memcmp(rsdp->Signature, "RSD PTR ", 8) != 0) { HalDbgPrint("HAL: Invalid RSDP signature!\n"); return; } ACPISDTHeader* sdtHeader = NULL; uint32_t count = 0; int isXsdt = 0; // Safely find and validate the XSDT (ACPI 2.0+) if (rsdp->Revision >= 2 && rsdp->XsdtAddress != 0) { XSDT* xsdt = (XSDT*)(rsdp->XsdtAddress + gPhysBase); if (validate_checksum(xsdt, xsdt->Header.Length) && memcmp(xsdt->Header.Signature, "XSDT", 4) == 0) { sdtHeader = &xsdt->Header; count = (xsdt->Header.Length - sizeof(ACPISDTHeader)) / 8; isXsdt = 1; } } // Fallback to safely finding and validating the RSDT (ACPI 1.0) if (sdtHeader == NULL && rsdp->RsdtAddress != 0) { RSDT* rsdt = (RSDT*)((uintptr_t)rsdp->RsdtAddress + gPhysBase); if (validate_checksum(rsdt, rsdt->Header.Length) && memcmp(rsdt->Header.Signature, "RSDT", 4) == 0) { sdtHeader = &rsdt->Header; count = (rsdt->Header.Length - sizeof(ACPISDTHeader)) / 4; isXsdt = 0; } } if (sdtHeader == NULL) { HalDbgPrint("HAL: No valid XSDT or RSDT found!\n"); return; } // Walk the tables for (uint32_t i = 0; i < count; i++) { uintptr_t tablePhys = 0; if (isXsdt) { tablePhys = ((XSDT*)sdtHeader)->PointerToOtherSDT[i]; } else { tablePhys = ((RSDT*)sdtHeader)->PointerToOtherSDT[i]; } // Paranoid alignment check to reject firmware garbage pointers if (!tablePhys || (tablePhys & 3) != 0) continue; ACPISDTHeader* header = (ACPISDTHeader*)(tablePhys + gPhysBase); // Ensure the table isn't corrupt before parsing if (!validate_checksum(header, header->Length)) continue; // Parse MADT if (memcmp(header->Signature, "APIC", 4) == 0) { ParseMadt((MADT*)header); } // Parse FADT (signature "FACP") for PM1 control ports and DSDT pointer else if (memcmp(header->Signature, "FACP", 4) == 0) { ParseFadt((const FADT*)header); } // Parse HPET else if (memcmp(header->Signature, "HPET", 4) == 0) { // HPET base address block is located at offset 44 // (which is 8 bytes past the 36-byte ACPISDTHeader) gHpetPhys = *(uint64_t*)((uint8_t*)header + 44) & ~0xFFFULL; } } } // --------------------------------------------------------------------------- // HalShutdown - ACPI S5 soft power-off. Does not return. // --------------------------------------------------------------------------- #include static void HalShutdown(void) { _disable(); // SLP_EN (bit 13) | SLP_TYPx (bits 12:10) uint16_t valA = (uint16_t)((gSlpTypA << 10) | (1 << 13)); uint16_t valB = (uint16_t)((gSlpTypB << 10) | (1 << 13)); if (gPm1aCntBlk) __outword(gPm1aCntBlk, valA); if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB); // Firmware should have powered off by now. If it didn't, halt. while (true) __halt(); } // --------------------------------------------------------------------------- __declspec(dllexport) KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) { if (gInitialised) return STATUS_ALREADY_EXISTS; gPhysBase = initParams->PhysicalWindowBase; gApi.PhysToVirt = HalPhysToVirt; debugEnabled = initParams->DebugEnabled; // Screen first so DbgPrint works immediately. HalScreenInitialize(initParams->FramebufferBase + initParams->PhysicalWindowBase, (int)initParams->FramebufferWidth, (int)initParams->FramebufferHeight, (int)initParams->FramebufferStride, &gApi.Screen); printf_set_putchar(hal_putchar); HalDbgPrint("HAL: screen %dx%d\n", initParams->FramebufferWidth, initParams->FramebufferHeight); // Parse ACPI to find LAPIC / IOAPIC / HPET addresses. if (initParams->RsdpPhysical) WalkAcpi(initParams->RsdpPhysical); HalDbgPrint("HAL: LAPIC=%016llx IOAPIC=%016llx HPET=%016llx\n", gLapicPhys, gIoapicPhys, gHpetPhys); HalApicInitialize(gLapicPhys, gIoapicPhys); HalKeyboardInitialize(&gApi.Keyboard); KSTATUS ts = HalTimerInitialize(gLapicPhys, gHpetPhys, &gApi.Timer); if (!KSUCCESS(ts)) HalDbgPrint("HAL: HPET unavailable, using fallback\n"); HalPciInitialize(&gApi.Pci); HalDiskInitialize(&gApi.Disk); gApi.Intc.RouteIrq = HalApicRouteIrq; gApi.Intc.MaskIrq = HalApicMaskIrq; gApi.Intc.UnmaskIrq = HalApicUnmaskIrq; gApi.Intc.SendEoi = HalApicSendEoi; gApi.Power.Shutdown = HalShutdown; gApi.Version = HAL_API_VERSION; gInitialised = 1; HalDbgPrint("HAL: initialisation complete.\n"); return STATUS_SUCCESS; } __declspec(dllexport) void HalRegisterScheduler(void (*pKeSchedulerTick)(void)) { gTimerCallback = pKeSchedulerTick; } __declspec(dllexport) const HAL_API* HalGetApi(void) { return &gApi; }