0ad/source/lib/sysdep/acpi.cpp
janwas c9f10be090 acpi and mahaf: now set ModuleInitState to error if something fails (prevents a subsequent init from returning true and allowing its dependent to continue and crash into a wall)
wtime: "implement" CLOCK_MONOTONIC (that's what the timer already did,
anyhow)

main: move early init to GameSetup!EarlyInit (fixes atlas problem with
timer)

This was SVN commit r5101.
2007-05-26 23:29:20 +00:00

266 lines
6.0 KiB
C++

#include "precompiled.h"
#include "acpi.h"
#include "win/mahaf.h"
#include "lib/sysdep/cpu.h"
#include "lib/module_init.h"
#pragma pack(1)
//-----------------------------------------------------------------------------
// table utility functions
//-----------------------------------------------------------------------------
// return 8-bit checksum of a buffer (should be 0)
static u8 ComputeChecksum(const void* buf, size_t numBytes)
{
u8 sum = 0;
const u8* end = (const u8*)buf+numBytes;
for(const u8* p = (const u8*)buf; p < end; p++)
sum += *p;
return sum;
}
// caller is responsible for verifying the table is valid and must
// free() the returned pointer.
static const AcpiTable* AllocateCopyOfTable(u64 physicalAddress)
{
// 4 KiB ought to be enough; if not, the table will be re-mapped.
const size_t initialSize = 4*KiB;
const AcpiTable* mappedTable = (const AcpiTable*)mahaf_MapPhysicalMemory(physicalAddress, initialSize);
if(!mappedTable)
return 0;
const size_t size = mappedTable->size;
if(size > initialSize)
{
// re-map with correct size
mahaf_UnmapPhysicalMemory((void*)mappedTable);
mappedTable = (const AcpiTable*)mahaf_MapPhysicalMemory(physicalAddress, size);
if(!mappedTable)
return 0;
}
AcpiTable* table = (AcpiTable*)malloc(size);
if(table)
cpu_memcpy(table, mappedTable, size);
mahaf_UnmapPhysicalMemory((void*)mappedTable);
return table;
}
static bool VerifyTable(const AcpiTable* table, const char* signature = 0)
{
// caller knowns the signature; make sure it matches
if(signature)
{
if(memcmp(table->signature, signature, 4) != 0)
return false;
}
// no specific signature is called for; just make sure it's 4 letters
else
{
for(int i = 0; i < 4; i++)
{
if(!isalpha(table->signature[i]))
return false;
}
}
// must be at least as large as the common header
if(table->size < sizeof(AcpiTable))
return false;
// checksum of table must be 0
// .. AMIBIOS OEMB table has an incorrect checksum (off-by-one),
// so don't complain about any OEM tables (ignored anyway).
const bool isOemTable = (memcmp(table->signature, "OEM", 3) == 0);
if(ComputeChecksum(table, table->size) != 0 && !isOemTable)
return false;
return true;
}
//-----------------------------------------------------------------------------
// Root System Descriptor Pointer
//-----------------------------------------------------------------------------
struct RSDP
{
char signature[8]; // "RSD PTR "
u8 checksum; // sum of this struct = 0
char oemId[6];
u8 revision; // 0 for 1.0, 2 for 2.0
u32 rsdtPhysicalAddress;
};
static const size_t RSDP_ALIGNMENT = 16;
static const RSDP* LocateRsdp(const u8* buf, size_t numBytes)
{
const u8* const end = buf+numBytes;
for(const u8* p = buf; p < end; p += RSDP_ALIGNMENT)
{
const RSDP* rsdp = (const RSDP*)p;
if(memcmp(rsdp->signature, "RSD PTR ", 8) != 0)
continue;
if(ComputeChecksum(p, sizeof(RSDP)) != 0)
continue;
return rsdp;
}
return 0;
}
static bool LocateAndRetrieveRsdp(uintptr_t physicalAddress, size_t numBytes, RSDP& rsdp)
{
void* virtualAddress = mahaf_MapPhysicalMemory(physicalAddress, numBytes);
const RSDP* prsdp = LocateRsdp((const u8*)virtualAddress, numBytes);
if(prsdp)
rsdp = *prsdp; // stash in output parameter before unmapping
mahaf_UnmapPhysicalMemory(virtualAddress);
return (prsdp != 0);
}
static uintptr_t LocateEbdaPhysicalAddress()
{
struct BiosDataArea
{
u16 serialBase[4];
u16 parallelBase[3];
u16 ebdaSegment;
// ...
};
const BiosDataArea* bda = (const BiosDataArea*)mahaf_MapPhysicalMemory(0x400, 0x100);
if(!bda)
return 0;
const uintptr_t ebdaPhysicalAddress = ((uintptr_t)bda->ebdaSegment) * 16;
return ebdaPhysicalAddress;
}
static bool RetrieveRsdp(RSDP& rsdp)
{
// See ACPIspec30b, section 5.2.5.1:
// RSDP is either in the first KIB of the extended BIOS data area,
if(LocateAndRetrieveRsdp(LocateEbdaPhysicalAddress(), 1*KiB, rsdp))
return true;
// or in read-only BIOS memory.
if(LocateAndRetrieveRsdp(0xE0000, 0x20000, rsdp))
return true;
return false; // not found
}
//-----------------------------------------------------------------------------
// Root System Descriptor Table
struct RSDT
{
AcpiTable header;
u32 tables[1];
};
// avoid std::map et al. because we are called before _cinit
static const AcpiTable** tables;
static size_t numTables;
static bool LatchAllTables()
{
RSDP rsdp;
if(!RetrieveRsdp(rsdp))
return false;
const RSDT* rsdt = (const RSDT*)AllocateCopyOfTable(rsdp.rsdtPhysicalAddress);
if(!rsdt)
return false;
if(!VerifyTable((const AcpiTable*)rsdt, "RSDT"))
{
free((void*)rsdt);
return false;
}
numTables = (rsdt->header.size - sizeof(AcpiTable)) / sizeof(rsdt->tables[0]);
tables = new const AcpiTable*[numTables];
for(size_t i = 0; i < numTables; i++)
{
const AcpiTable* table = AllocateCopyOfTable(rsdt->tables[i]);
if(!table)
continue;
if(!VerifyTable(table))
debug_warn("invalid ACPI table");
tables[i] = table; // transfers ownership
}
free((void*)rsdt);
return true;
}
static void FreeAllTables()
{
for(size_t i = 0; i < numTables; i++)
{
SAFE_FREE(tables[i]);
}
delete[] tables;
}
const AcpiTable* acpi_GetTable(const char* signature)
{
// (typically only a few tables, linear search is OK)
for(size_t i = 0; i < numTables; i++)
{
const AcpiTable* table = tables[i];
if(strncmp(table->signature, signature, 4) == 0)
return table;
}
return 0;
}
//-----------------------------------------------------------------------------
static ModuleInitState initState;
bool acpi_Init()
{
if(!ModuleShouldInitialize(&initState))
return true;
if(!mahaf_Init())
{
ModuleSetError(&initState);
return false;
}
LatchAllTables();
return true;
}
void acpi_Shutdown()
{
if(!ModuleShouldShutdown(&initState))
return;
FreeAllTables();
mahaf_Shutdown();
}