// This file is part of the Essence operating system. // It is released under the terms of the MIT license -- see LICENSE.md. // Written by: nakst. #define SIGNATURE_RSDP (0x2052545020445352) #define SIGNATURE_RSDT (0x54445352) #define SIGNATURE_XSDT (0x54445358) #define SIGNATURE_MADT (0x43495041) #define SIGNATURE_FADT (0x50434146) #define SIGNATURE_HPET (0x54455048) struct RootSystemDescriptorPointer { uint64_t signature; 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]; }; struct ACPIDescriptorTable { #define ACPI_DESCRIPTOR_TABLE_HEADER_LENGTH (36) uint32_t signature; uint32_t length; uint64_t id; uint64_t tableID; uint32_t oemRevision; uint32_t creatorID; uint32_t creatorRevision; }; struct MultipleAPICDescriptionTable { uint32_t lapicAddress; uint32_t flags; }; struct ArchCPU { uint8_t processorID, kernelProcessorID; uint8_t apicID; bool bootProcessor; void **kernelStack; CPULocalStorage *local; }; struct ACPIIoApic { uint8_t id; uint32_t volatile *address; uint32_t gsiBase; }; struct ACPIInterruptOverride { uint8_t sourceIRQ; uint32_t gsiNumber; bool activeLow, levelTriggered; }; struct ACPILapicNMI { uint8_t processor; // 0xFF for all processors uint8_t lintIndex; bool activeLow, levelTriggered; }; struct ACPI { size_t processorCount; size_t ioapicCount; size_t interruptOverrideCount; size_t lapicNMICount; ArchCPU processors[256]; ACPIIoApic ioApics[16]; ACPIInterruptOverride interruptOverrides[256]; ACPILapicNMI lapicNMIs[32]; RootSystemDescriptorPointer *rsdp; ACPIDescriptorTable *madt; volatile uint32_t *lapicAddress; size_t lapicTicksPerMs; bool ps2ControllerUnavailable; bool vgaControllerUnavailable; uint8_t centuryRegisterIndex; volatile uint64_t *hpetBaseAddress; uint64_t hpetPeriod; // 10^-15 seconds. KDevice *computer; }; ACPI acpi; uint32_t ACPIIoApicReadRegister(ACPIIoApic *apic, uint32_t reg) { apic->address[0] = reg; return apic->address[4]; } void ACPIIoApicWriteRegister(ACPIIoApic *apic, uint32_t reg, uint32_t value) { apic->address[0] = reg; apic->address[4] = value; } void ACPICheckTable(const ACPIDescriptorTable *table) { if (!EsMemorySumBytes((uint8_t *) table, table->length)) { return; } KernelPanic("ACPICheckTable - ACPI table with signature %s had invalid checksum: " "length: %D, ID = %s, table = %s, OEM revision = %d, creator = %s, creator revision = %d.\n", 4, &table->signature, table->length, 8, &table->id, 8, &table->tableID, table->oemRevision, 4, &table->creatorID, table->creatorRevision); } void *ACPIMapPhysicalMemory(uintptr_t physicalAddress, size_t length) { return MMMapPhysical(kernelMMSpace, physicalAddress, length, MM_REGION_NOT_CACHEABLE); } void KPS2SafeToInitialise() { // This is only called when either: // - the PCI driver determines there are no USB controllers // - the USB controller disables USB emulation // TODO Qemu sets this to true? #if 0 if (acpi.ps2ControllerUnavailable) { return; } #endif KThreadCreate("InitPS2", [] (uintptr_t) { KDeviceAttachByName(acpi.computer, "PS2"); }); } void *ACPIGetRSDP() { return acpi.rsdp; } uint8_t ACPIGetCenturyRegisterIndex() { return acpi.centuryRegisterIndex; } void ACPIParseTables() { acpi.rsdp = (RootSystemDescriptorPointer *) MMMapPhysical(kernelMMSpace, ArchFindRootSystemDescriptorPointer(), 16384, ES_FLAGS_DEFAULT); ACPIDescriptorTable *madtHeader = nullptr; ACPIDescriptorTable *sdt = nullptr; bool isXSDT = false; if (acpi.rsdp) { if (acpi.rsdp->revision == 2 && acpi.rsdp->xsdtAddress) { isXSDT = true; sdt = (ACPIDescriptorTable *) acpi.rsdp->xsdtAddress; } else { isXSDT = false; sdt = (ACPIDescriptorTable *) (uintptr_t) acpi.rsdp->rsdtAddress; } sdt = (ACPIDescriptorTable *) MMMapPhysical(kernelMMSpace, (uintptr_t) sdt, 16384, ES_FLAGS_DEFAULT); } else { KernelPanic("ACPIInitialise - Could not find supported root system descriptor pointer.\nACPI support is required.\n"); } if (((sdt->signature == SIGNATURE_XSDT && isXSDT) || (sdt->signature == SIGNATURE_RSDT && !isXSDT)) && sdt->length < 16384 && !EsMemorySumBytes((uint8_t *) sdt, sdt->length)) { // The SDT is valid. } else { KernelPanic("ACPIInitialise - Could not find a valid or supported system descriptor table.\nACPI support is required.\n"); } size_t tablesCount = (sdt->length - sizeof(ACPIDescriptorTable)) >> (isXSDT ? 3 : 2); if (tablesCount < 1) { KernelPanic("ACPIInitialise - The system descriptor table contains an unsupported number of tables (%d).\n", tablesCount); } uintptr_t tableListAddress = (uintptr_t) sdt + ACPI_DESCRIPTOR_TABLE_HEADER_LENGTH; KernelLog(LOG_INFO, "ACPI", "table count", "ACPIInitialise - Found %d tables.\n", tablesCount); for (uintptr_t i = 0; i < tablesCount; i++) { uintptr_t address; if (isXSDT) { address = ((uint64_t *) tableListAddress)[i]; } else { address = ((uint32_t *) tableListAddress)[i]; } ACPIDescriptorTable *header = (ACPIDescriptorTable *) MMMapPhysical(kernelMMSpace, address, sizeof(ACPIDescriptorTable), ES_FLAGS_DEFAULT); KernelLog(LOG_INFO, "ACPI", "table enumerated", "ACPIInitialise - Found ACPI table '%s'.\n", 4, &header->signature); if (header->signature == SIGNATURE_MADT) { madtHeader = (ACPIDescriptorTable *) MMMapPhysical(kernelMMSpace, address, header->length, ES_FLAGS_DEFAULT); ACPICheckTable(madtHeader); } else if (header->signature == SIGNATURE_FADT) { ACPIDescriptorTable *fadt = (ACPIDescriptorTable *) MMMapPhysical(kernelMMSpace, address, header->length, ES_FLAGS_DEFAULT); ACPICheckTable(fadt); if (header->length > 109) { acpi.centuryRegisterIndex = ((uint8_t *) fadt)[108]; uint8_t bootArchitectureFlags = ((uint8_t *) fadt)[109]; acpi.ps2ControllerUnavailable = ~bootArchitectureFlags & (1 << 1); acpi.vgaControllerUnavailable = bootArchitectureFlags & (1 << 2); KernelLog(LOG_INFO, "ACPI", "FADT", "PS/2 controller is %z; VGA controller is %z.\n", acpi.ps2ControllerUnavailable ? "unavailble" : "present", acpi.vgaControllerUnavailable ? "unavailble" : "present"); } MMFree(kernelMMSpace, fadt); } else if (header->signature == SIGNATURE_HPET) { ACPIDescriptorTable *hpet = (ACPIDescriptorTable *) MMMapPhysical(kernelMMSpace, address, header->length, ES_FLAGS_DEFAULT); ACPICheckTable(hpet); if (header->length > 52 && ((uint8_t *) header)[52] == 0) { uint64_t baseAddress; EsMemoryCopy(&baseAddress, (uint8_t *) header + 44, sizeof(uint64_t)); KernelLog(LOG_INFO, "ACPI", "HPET", "Found primary HPET with base address %x.\n", baseAddress); acpi.hpetBaseAddress = (uint64_t *) MMMapPhysical(kernelMMSpace, baseAddress, 1024, ES_FLAGS_DEFAULT); if (acpi.hpetBaseAddress) { acpi.hpetBaseAddress[2] |= 1; // Start the main counter. acpi.hpetPeriod = acpi.hpetBaseAddress[0] >> 32; uint8_t revisionID = acpi.hpetBaseAddress[0] & 0xFF; uint64_t initialCount = acpi.hpetBaseAddress[30]; KernelLog(LOG_INFO, "ACPI", "HPET", "HPET has period of %d fs, revision ID %d, and initial count %d.\n", acpi.hpetPeriod, revisionID, initialCount); } } MMFree(kernelMMSpace, hpet); } MMFree(kernelMMSpace, header); } MultipleAPICDescriptionTable *madt = (MultipleAPICDescriptionTable *) ((uint8_t *) madtHeader + ACPI_DESCRIPTOR_TABLE_HEADER_LENGTH); if (!madt) { KernelPanic("ACPIInitialise - Could not find the MADT table.\nThis is required to use the APIC.\n"); } uintptr_t length = madtHeader->length - ACPI_DESCRIPTOR_TABLE_HEADER_LENGTH - sizeof(MultipleAPICDescriptionTable); uintptr_t startLength = length; uint8_t *data = (uint8_t *) (madt + 1); #ifdef ES_ARCH_X86_64 acpi.lapicAddress = (uint32_t volatile *) ACPIMapPhysicalMemory(madt->lapicAddress, 0x10000); #endif while (length && length <= startLength) { uint8_t entryType = data[0]; uint8_t entryLength = data[1]; switch (entryType) { case 0: { // A processor and its LAPIC. if ((data[4] & 1) == 0) goto nextEntry; ArchCPU *processor = acpi.processors + acpi.processorCount; processor->processorID = data[2]; processor->apicID = data[3]; acpi.processorCount++; } break; case 1: { // An I/O APIC. acpi.ioApics[acpi.ioapicCount].id = data[2]; acpi.ioApics[acpi.ioapicCount].address = (uint32_t volatile *) ACPIMapPhysicalMemory(((uint32_t *) data)[1], 0x10000); ACPIIoApicReadRegister(&acpi.ioApics[acpi.ioapicCount], 0); // Make sure it's mapped. acpi.ioApics[acpi.ioapicCount].gsiBase = ((uint32_t *) data)[2]; acpi.ioapicCount++; } break; case 2: { // An interrupt source override structure. acpi.interruptOverrides[acpi.interruptOverrideCount].sourceIRQ = data[3]; acpi.interruptOverrides[acpi.interruptOverrideCount].gsiNumber = ((uint32_t *) data)[1]; acpi.interruptOverrides[acpi.interruptOverrideCount].activeLow = (data[8] & 2) ? true : false; acpi.interruptOverrides[acpi.interruptOverrideCount].levelTriggered = (data[8] & 8) ? true : false; KernelLog(LOG_INFO, "ACPI", "interrupt override", "ACPIInitialise - Source IRQ %d is mapped to GSI %d%z%z.\n", acpi.interruptOverrides[acpi.interruptOverrideCount].sourceIRQ, acpi.interruptOverrides[acpi.interruptOverrideCount].gsiNumber, acpi.interruptOverrides[acpi.interruptOverrideCount].activeLow ? ", active low" : ", active high", acpi.interruptOverrides[acpi.interruptOverrideCount].levelTriggered ? ", level triggered" : ", edge triggered"); acpi.interruptOverrideCount++; } break; case 4: { // A non-maskable interrupt. acpi.lapicNMIs[acpi.lapicNMICount].processor = data[2]; acpi.lapicNMIs[acpi.lapicNMICount].lintIndex = data[5]; acpi.lapicNMIs[acpi.lapicNMICount].activeLow = (data[3] & 2) ? true : false; acpi.lapicNMIs[acpi.lapicNMICount].levelTriggered = (data[3] & 8) ? true : false; acpi.lapicNMICount++; } break; default: { KernelLog(LOG_ERROR, "ACPI", "unrecognised MADT entry", "ACPIInitialise - Found unknown entry of type %d in MADT\n", entryType); } break; } nextEntry: length -= entryLength; data += entryLength; } if (acpi.processorCount > 256 || acpi.ioapicCount > 16 || acpi.interruptOverrideCount > 256 || acpi.lapicNMICount > 32) { KernelPanic("ACPIInitialise - Invalid number of processors (%d/%d), \n" " I/O APICs (%d/%d), interrupt overrides (%d/%d)\n" " and LAPIC NMIs (%d/%d)\n", acpi.processorCount, 256, acpi.ioapicCount, 16, acpi.interruptOverrideCount, 256, acpi.lapicNMICount, 32); } } size_t KGetCPUCount() { return acpi.processorCount; } CPULocalStorage *KGetCPULocal(uintptr_t index) { return acpi.processors[index].local; } #ifdef USE_ACPICA #include "acpica.cpp" #else void ArchShutdown() { if (shutdownAction == ES_SHUTDOWN_ACTION_RESTART) ProcessorReset(); StartDebugOutput(); EsPrint("\nIt's now safe to turn off your computer.\n"); ProcessorDisableInterrupts(); ProcessorHalt(); } EsError KACPIObjectSetDeviceNotificationHandler(KACPIObject *, KACPINotificationHandler, EsGeneric) { return ES_ERROR_UNSUPPORTED; } EsError KACPIObjectEvaluateInteger(KACPIObject *, const char *, uint64_t *) { return ES_ERROR_UNSUPPORTED; } EsError KACPIObjectEvaluateMethodWithInteger(KACPIObject *, const char *, uint64_t) { return ES_ERROR_UNSUPPORTED; } #endif void ACPIDeviceAttach(KDevice *parentDevice) { acpi.computer = KDeviceCreate("ACPI computer", parentDevice, sizeof(KDevice)); KThreadCreate("InitACPI", [] (uintptr_t) { KDeviceAttachByName(acpi.computer, "RTC"); #ifdef USE_ACPICA ACPICAInitialise(); #endif #ifdef USE_SMP ArchStartupApplicationProcessors(); #endif }); if (!acpi.vgaControllerUnavailable) { KDeviceAttachByName(acpi.computer, "SVGA"); } KDeviceAttachByName(acpi.computer, "PCI"); } KDriver driverACPI = { .attach = ACPIDeviceAttach, };