/** * PROJECT: ExectOS * COPYRIGHT: See COPYING.md in the top level directory * FILE: xtldr/modules/acpi/acpi.c * DESCRIPTION: XTLDR ACPI Support Module * DEVELOPERS: Rafal Kupiec */ #include /* Dummy module information */ MODULE_AUTHOR(L"Rafal Kupiec "); MODULE_DESCRIPTION(L"ACPI support"); MODULE_LICENSE(L"GPLv3"); MODULE_VERSION(L"0.1"); /** * Attempts to get XSDP. If it is not found or checksum mismatch, it will try to get RSDP instead. * * @param AcpiTable * Suplies a pointer to memory area where XSDP or RSRP address will be stored. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS AcGetAcpiDescriptionPointer(OUT PVOID *AcpiTable) { PVOID Rsdp; /* Try to get XSDP (ACPI 2.0) from system configuration tables */ if(AcGetXsdpTable(&Rsdp) == STATUS_EFI_SUCCESS) { /* XSDP found, return success */ *AcpiTable = Rsdp; return STATUS_EFI_SUCCESS; } /* Try to get RSDP (ACPI 1.0) from system configuration tables */ if(AcGetRsdpTable(&Rsdp) == STATUS_EFI_SUCCESS) { /* RSDP found, return success */ *AcpiTable = Rsdp; return STATUS_EFI_SUCCESS; } /* Neither XSDP nor RSDP found */ return STATUS_EFI_NOT_FOUND; } /** * Finds ACPI description table with given signature. * * @param Signature * Supplies the signature of the desired ACPI table. * * @param PreviousTable * Supplies a pointer to the table to start searching from. * * @param AcpiTable * Supplies a pointer to memory area where ACPI table address will be stored, or NULL if not found. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS AcGetAcpiTable(IN CONST UINT Signature, IN PVOID PreviousTable, OUT PVOID *AcpiTable) { PACPI_DESCRIPTION_HEADER TableHeader; SIZE_T RsdtIndex, TableIndex; EFI_STATUS Status; SIZE_T TableCount; PACPI_RSDP Rsdp; PACPI_RSDT Rsdt; BOOLEAN Xsdp; /* Return NULL address by default if requested table not found */ *AcpiTable = NULL; /* Get Root System Description Table Pointer */ Status = AcGetAcpiDescriptionPointer((PVOID)&Rsdp); if(Status != STATUS_EFI_SUCCESS) { /* ACPI tables not found, return error */ return Status; } /* Check if it is XSDP (ACPI 2.0) or RSDP (ACPI 1.0) */ if(Rsdp->Revision >= 2 && Rsdp->XsdtAddress) { /* XSDP (ACPI 2.0) */ Xsdp = TRUE; Rsdt = (PACPI_RSDT)(UINT_PTR)Rsdp->XsdtAddress; TableCount = (Rsdt->Header.Length - sizeof(ACPI_DESCRIPTION_HEADER)) / 8; } else { /* RSDP (ACPI 1.0) */ Xsdp = FALSE; Rsdt = (PACPI_RSDT)(UINT_PTR)Rsdp->RsdtAddress; TableCount = (Rsdt->Header.Length - sizeof(ACPI_DESCRIPTION_HEADER)) / 4; } /* Iterate over all ACPI tables */ for(TableIndex = 0; TableIndex < TableCount; TableIndex++) { /* Get table headers in reverse order */ RsdtIndex = TableCount - TableIndex - 1; /* Check if XSDP or RSDT is used */ if(Xsdp) { /* Get table header from XSDT */ TableHeader = (PACPI_DESCRIPTION_HEADER)(ULONG_PTR)((PULONGLONG)Rsdt->Tables)[RsdtIndex]; } else { /* Get table header from RSDT */ TableHeader = (PACPI_DESCRIPTION_HEADER)(ULONG_PTR)((PULONG)Rsdt->Tables)[RsdtIndex]; } /* Check if previous table provided */ if(PreviousTable != NULL) { /* Check if this is a table previously found */ if(TableHeader == (PVOID)PreviousTable) { /* Unset previous table */ PreviousTable = NULL; } /* Skip to next ACPI table */ continue; } /* Verify table signature */ if((TableHeader->Signature == Signature)) { /* Found requested ACPI table */ break; } } /* Make sure table was found */ if(TableHeader->Signature != Signature) { /* ACPI table not found, return error */ return STATUS_EFI_NOT_FOUND; } /* Don't validate FADT on old, broken firmwares with ACPI 2.0 or older */ if(TableHeader->Signature != ACPI_FADT_SIGNATURE || TableHeader->Revision > 2) { /* Validate table checksum */ if(!AcpValidateAcpiTable(TableHeader, TableHeader->Length)) { /* Checksum mismatch, return error */ return STATUS_EFI_CRC_ERROR; } } /* Found valid ACPI table, return success */ *AcpiTable = TableHeader; return STATUS_EFI_SUCCESS; } /** * Gets the Advanced Programmable Interrupt Controller (APIC) base address. * * @param ApicBase * Supplies a pointer to memory area where APIC base address will be stored. * * @return This routine returns an EFI status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS AcGetApicBase(OUT PVOID *ApicBase) { PCPUID_REGISTERS CpuRegisters = NULL; /* Get CPU features list */ CpuRegisters->Leaf = CPUID_GET_CPU_FEATURES; CpuRegisters->SubLeaf = 0; CpuRegisters->Eax = 0; CpuRegisters->Ebx = 0; CpuRegisters->Ecx = 0; CpuRegisters->Edx = 0; ArCpuId(CpuRegisters); /* Check if APIC present */ if((CpuRegisters->Edx & CPUID_FEATURES_EDX_APIC) == 0) { /* APIC is not supported by the CPU */ return STATUS_EFI_UNSUPPORTED; } /* Get APIC base address */ *ApicBase = (PVOID)((UINT_PTR)ArReadModelSpecificRegister(0x1B) & 0xFFFFF000); /* Return success */ return STATUS_EFI_SUCCESS; } /** * Gets RSDP (ACPI 1.0) from EFI system configuration * * @param AcpiTable * Suplies a pointer to memory area where RSDP address will be stored. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS AcGetRsdpTable(OUT PVOID *AcpiTable) { EFI_GUID AcpiGuid = EFI_CONFIG_TABLE_ACPI_TABLE_GUID; EFI_STATUS Status; PVOID RsdpTable; /* Get RSDP (ACPI 1.0) table from system configuration tables */ Status = XtLdrProtocol->Util.GetConfigurationTable(&AcpiGuid, &RsdpTable); if(Status != STATUS_EFI_SUCCESS || !AcpValidateAcpiTable(RsdpTable, 20)) { /* RSDP not found or checksum mismatch */ *AcpiTable = NULL; return STATUS_EFI_NOT_FOUND; } /* RSDP found, return success */ *AcpiTable = RsdpTable; return STATUS_EFI_SUCCESS; } /** * Gets SMBIOS from EFI system configuration * * @param SmBiosTable * Suplies a pointer to memory area where SMBIOS address will be stored. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS AcGetSMBiosTable(OUT PVOID *SmBiosTable) { EFI_GUID SmBiosGuid = EFI_CONFIG_TABLE_SMBIOS_TABLE_GUID; PSMBIOS_TABLE_HEADER SmBios; EFI_STATUS Status; /* Get SMBIOS table from system configuration tables */ Status = XtLdrProtocol->Util.GetConfigurationTable(&SmBiosGuid, (PVOID)&SmBios); if(Status != STATUS_EFI_SUCCESS || !AcpValidateAcpiTable(SmBios, SmBios->Length)) { /* SMBIOS not found or checksum mismatch */ *SmBiosTable = NULL; return STATUS_EFI_NOT_FOUND; } /* SMBIOS found, return success */ *SmBiosTable = SmBios; return STATUS_EFI_SUCCESS; } /** * Gets SMBIOS3 from EFI system configuration * * @param SmBiosTable * Suplies a pointer to memory area where SMBIOS3 address will be stored. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS AcGetSMBios3Table(OUT PVOID *SmBiosTable) { EFI_GUID SmBios3Guid = EFI_CONFIG_TABLE_SMBIOS3_TABLE_GUID; PSMBIOS3_TABLE_HEADER SmBios; EFI_STATUS Status; /* Get SMBIOS3 table from system configuration tables */ Status = XtLdrProtocol->Util.GetConfigurationTable(&SmBios3Guid, (PVOID)&SmBios); if(Status != STATUS_EFI_SUCCESS || !AcpValidateAcpiTable(SmBios, SmBios->Length)) { /* SMBIOS3 not found or checksum mismatch */ *SmBiosTable = NULL; return STATUS_EFI_NOT_FOUND; } /* SMBIOS3 found, return success */ *SmBiosTable = SmBios; return STATUS_EFI_SUCCESS; } /** * Gets XSDP (ACPI 2.0) from EFI system configuration * * @param AcpiTable * Suplies a pointer to memory area where XSDP address will be stored. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS AcGetXsdpTable(OUT PVOID *AcpiTable) { EFI_GUID AcpiGuid = EFI_CONFIG_TABLE_ACPI20_TABLE_GUID; EFI_STATUS Status; PVOID XsdpTable; /* Get XSDP (ACPI 2.0) from system configuration tables */ Status = XtLdrProtocol->Util.GetConfigurationTable(&AcpiGuid, &XsdpTable); if(Status != STATUS_EFI_SUCCESS || !AcpValidateAcpiTable(XsdpTable, 36)) { /* XSDP not found or checksum mismatch */ *AcpiTable = NULL; return STATUS_EFI_NOT_FOUND; } /* XSDP found, return success */ *AcpiTable = XsdpTable; return STATUS_EFI_SUCCESS; } /** * Validates given ACPI table by calculating its checksum. * * @param Buffer * Supplies a pointer to the table to checksum. * * @param Size * Supplies the size of the table, in bytes. * * @return This routine returns TRUE if the table is valid, or FALSE otherwise. * * @since XT 1.0 */ XTCDECL BOOLEAN AcpValidateAcpiTable(IN PVOID Buffer, IN UINT_PTR Size) { PUCHAR Pointer; UCHAR Sum; /* Initialize variables */ Sum = 0; Pointer = Buffer; /* Calculate checksum of given table */ while(Size != 0) { Sum = (UCHAR)(Sum + *Pointer); Pointer += 1; Size -= 1; } /* Return calculated checksum */ return (Sum == 0) ? TRUE : FALSE; } /** * This routine is the entry point of the XT EFI boot loader module. * * @param ImageHandle * Firmware-allocated handle that identifies the image. * * @param SystemTable * Provides the EFI system table. * * @return This routine returns status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS XtLdrModuleMain(IN EFI_HANDLE ImageHandle, IN PEFI_SYSTEM_TABLE SystemTable) { EFI_GUID Guid = XT_ACPI_PROTOCOL_GUID; EFI_STATUS Status; /* Open the XTLDR protocol */ Status = BlGetXtLdrProtocol(SystemTable, ImageHandle, &XtLdrProtocol); if(Status != STATUS_EFI_SUCCESS) { /* Failed to open the protocol, return error */ return STATUS_EFI_PROTOCOL_ERROR; } /* Set routines available via ACPI protocol */ AcpAcpiProtocol.GetAcpiDescriptionPointer = AcGetAcpiDescriptionPointer; AcpAcpiProtocol.GetAcpiTable = AcGetAcpiTable; AcpAcpiProtocol.GetApicBase = AcGetApicBase; AcpAcpiProtocol.GetRsdpTable = AcGetRsdpTable; AcpAcpiProtocol.GetSMBiosTable = AcGetSMBiosTable; AcpAcpiProtocol.GetSMBios3Table = AcGetSMBios3Table; AcpAcpiProtocol.GetXsdpTable = AcGetXsdpTable; /* Install ACPI protocol */ return XtLdrProtocol->Protocol.Install(&AcpAcpiProtocol, &Guid); }