#include <string.h>
#include <KernelExport.h>
#include <SupportDefs.h>
#include <arch_acpi.h>
#include <boot/stage2.h>
#include <boot/platform.h>
#include <boot/stdio.h>
#include "efi_platform.h"
#include "acpi.h"
#include "mmu.h"
#define TRACE_ACPI
#ifdef TRACE_ACPI
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
static acpi_descriptor_header* sAcpiRsdt;
static acpi_descriptor_header* sAcpiXsdt;
static int32 sNumEntries = -1;
static status_t
acpi_validate_rsdp(acpi_rsdp* rsdp)
{
const char* data = (const char*)rsdp;
unsigned char checksum = 0;
for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++)
checksum += data[i];
if ((checksum & 0xff) != 0) {
TRACE(("acpi: rsdp failed basic checksum\n"));
return B_BAD_DATA;
}
if (rsdp->revision > 0) {
for (uint32 i = sizeof(acpi_rsdp_legacy);
i < sizeof(acpi_rsdp_extended); i++) {
checksum += data[i];
}
if ((checksum & 0xff) != 0) {
TRACE(("acpi: rsdp failed extended checksum\n"));
return B_BAD_DATA;
}
}
return B_OK;
}
static status_t
acpi_validate_rsdt(acpi_descriptor_header* rsdt)
{
const char* data = (const char*)rsdt;
unsigned char checksum = 0;
for (uint32 i = 0; i < rsdt->length; i++)
checksum += data[i];
return checksum == 0 ? B_OK : B_BAD_DATA;
}
static status_t
acpi_check_rsdt(acpi_rsdp* rsdp)
{
if (acpi_validate_rsdp(rsdp) != B_OK)
return B_BAD_DATA;
bool usingXsdt = false;
TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n",
rsdp, rsdp->oem_id, rsdp->revision));
TRACE(("acpi: rsdp points to rsdt at 0x%" B_PRIx32 "\n", rsdp->rsdt_address));
acpi_descriptor_header* rsdt = NULL;
if (rsdp->revision > 0) {
rsdt = (acpi_descriptor_header*)(addr_t)rsdp->xsdt_address;
if (rsdt != NULL
&& strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) {
rsdt = NULL;
TRACE(("acpi: invalid extended system description table\n"));
} else
usingXsdt = true;
}
if (rsdt == NULL) {
rsdt = (acpi_descriptor_header*)(addr_t)rsdp->rsdt_address;
if (rsdt == NULL) {
TRACE(("acpi: couldn't map rsdt header\n"));
return B_ERROR;
}
if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
rsdt = NULL;
TRACE(("acpi: invalid root system description table\n"));
return B_ERROR;
}
TRACE(("acpi: rsdt length: %" B_PRIu32 "\n", rsdt->length));
}
if (rsdt != NULL) {
if (acpi_validate_rsdt(rsdt) != B_OK) {
TRACE(("acpi: rsdt failed checksum validation\n"));
return B_ERROR;
} else {
if (usingXsdt)
sAcpiXsdt = rsdt;
else
sAcpiRsdt = rsdt;
TRACE(("acpi: found valid %s at %p\n",
usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE,
rsdt));
}
} else
return B_ERROR;
return B_OK;
}
template<typename PointerType>
acpi_descriptor_header*
acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt)
{
if (acpiSdt == NULL)
return NULL;
if (sNumEntries == -1) {
sNumEntries = (acpiSdt->length - sizeof(acpi_descriptor_header))
/ sizeof(PointerType);
}
if (sNumEntries <= 0) {
TRACE(("acpi: root system description table is empty\n"));
return NULL;
}
TRACE(("acpi: searching %" B_PRId32 " entries for table '%.4s'\n", sNumEntries,
signature));
PointerType* pointer = (PointerType*)((uint8*)acpiSdt
+ sizeof(acpi_descriptor_header));
acpi_descriptor_header* header = NULL;
for (int32 j = 0; j < sNumEntries; j++, pointer++) {
header = (acpi_descriptor_header*)(addr_t)*pointer;
if (header != NULL && strncmp(header->signature, signature, 4) == 0) {
TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer));
return header;
}
TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n",
signature, header != NULL ? header->signature : "null"));
header = NULL;
continue;
}
return NULL;
}
acpi_descriptor_header*
acpi_find_table(const char* signature)
{
if (sAcpiRsdt != NULL)
return acpi_find_table_generic<uint32>(signature, sAcpiRsdt);
else if (sAcpiXsdt != NULL)
return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
return NULL;
}
void __attribute__((weak))
arch_handle_acpi()
{
}
void
acpi_init()
{
efi_guid acpi = ACPI_20_TABLE_GUID;
efi_configuration_table *table = kSystemTable->ConfigurationTable;
size_t entries = kSystemTable->NumberOfTableEntries;
for (uint32 i = 0; i < entries; i++) {
if (!table[i].VendorGuid.equals(acpi))
continue;
acpi_rsdp *rsdp = (acpi_rsdp *)(table[i].VendorTable);
if (strncmp((char *)rsdp, ACPI_RSDP_SIGNATURE, 8) == 0)
TRACE(("acpi_init: found ACPI RSDP signature at %p\n", rsdp));
if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) {
gKernelArgs.arch_args.acpi_root = rsdp;
arch_handle_acpi();
break;
}
}
}