ACPICA

From OSDev.wiki
Jump to navigation Jump to search

The ACPI Component Architecture ACPICA provides an operating system (OS)-independent reference implementation of the Advanced Configuration and Power Interface. It can be adapted to any host OS. The ACPICA code is meant to be directly integrated into the host OS, as a kernel-resident subsystem. Hosting the ACPICA requires no changes to the core ACPICA code. However, it does require a small OS-specific interface layer, which must be written specifically for each host OS.

The complexity of the ACPI specification leads to a lengthy and difficult implementation in OS software. The purpose of the ACPI Component Architecture is to simplify ACPI implementations for operating system vendors (OSVs) by providing major portions of an ACPI implementation in OS-independent ACPI modules that can be easily integrated into any OS.

As said before you need to implement yourself a few functions that are part of the OS interface layer (OSL). Here are those functions:

OS Layer

There are about 25-30 functions to be implemented:

Environmental and ACPI Tables

AcpiOsInitialize

ACPI_STATUS AcpiOsInitialize()

This is called during ACPICA Initialization. It gives the possibility to the OSL to initialize itself. Generally it should do nothing

AcpiOsTerminate

ACPI_STATUS AcpiOsTerminate()

This is called during ACPICA Shutdown (which is not the computer shutdown, just the Acpi). Here you can free any memory which was allocated in AcpiOsInitialize.

AcpiOsGetRootPointer

ACPI_PHYSICAL_ADDRESS AcpiOsGetRootPointer()

Strangely ACPICA leaves to you the job of finding the RSDP. However you can just do:

ACPI_PHYSICAL_ADDRESS AcpiOsGetRootPointer()
{
	ACPI_SIZE Ret;
	AcpiFindRootPointer(&Ret);
	return Ret;
}

where AcpiFindRootPointer is part of ACPICA itself.

AcpiOsPredefinedOverride

ACPI_STATUS AcpiOsPredefinedOverride(const ACPI_PREDEFINED_NAMES *PredefinedObject, ACPI_STRING *NewValue)

This function allows the host to override the predefined objects in the ACPI namespace. It is called when a new object is found in the Acpi namespace. However you can just put NULL in *NewValue and return.

AcpiOsTableOverride

ACPI_STATUS AcpiOsTableOverride(ACPI_TABLE_HEADER *ExistingTable, ACPI_TABLE_HEADER **NewTable)

The same of AcpiOsPredefinedOverride but for entire Acpi tables. You can replace them. Just put NULL in *NewValue and return.

Memory Management

AcpiOsMapMemory

void *AcpiOsMapMemory(ACPI_PHYSICAL_ADDRESS PhysicalAddress, ACPI_SIZE Length)

This is not really easy. ACPICA is asking you to map a physical address in the virtual address space. If you don't use paging, just return PhysicalAddress. You need:

  1. To round Length up to the size of a page (Length can be 2, 1024 for example)
  2. Find a range of virtual addresses where map the physical frames.
  3. Map the physical frames to the virtual addresses choosen.
  4. Return the virtual address plus the page offset of the physical address. (Eg. If you where asked to map 0x40E you have to return 0xF000040E and not just 0xF0000000)

AcpiOsUnmapMemory

void AcpiOsUnmapMemory(void *where, ACPI_SIZE length)

Unmap pages mapped using AcpiOsMapMemory. where is the Virtual address returned in AcpiOsMapMemory and length is equal to the length of the same function. Just remove the virtual address form the page directory and set that virtual address as reusable. Note: for the last two functions you might need a separated heap.

AcpiOsGetPhysicalAddress

ACPI_STATUS AcpiOsGetPhysicalAddress(void *LogicalAddress, ACPI_PHYSICAL_ADDRESS *PhysicalAddress)

Get the physical address pointed by LogicalAddress and put it in *PhysicalAddress. If you do not use paging just put LogicalAddress in *PhysicalAddress

AcpiOsAllocate

void *AcpiOsAllocate(ACPI_SIZE Size);

Dynamically allocate memory in the heap. Return NULL on error or end of memory. In other words:

return malloc(size)

AcpiOsFree

void AcpiOsFree(void *Memory);

Free previously dynamically allocated memory.

AcpiOsReadable

BOOLEAN AcpiOsReadable(void *Memory, ACPI_SIZE Length)

Check that the memory from Memory to (Memory + Length) is readable. This is never used (at least i did never see it used). However it should (on x86(_64)) check that a the locations of memory are present in the paging structure.

AcpiOsWritable

BOOLEAN AcpiOsWritable(void *Memory, ACPI_SIZE Length)

Check that the memory from Memory to (Memory + Length) is writable. This is never used (at least i did never see it used). However it should (on x86(_64)) check that a the locations of memory are present in the paging structure and that they have the Write flag set.

Caches

ACPICA uses caches to make things faster. You can use your kernel's cache or just let ACPICA use its internal cache. To do so just define in your platform/acwhatever.h file

#define ACPI_CACHE_T                ACPI_MEMORY_LIST
#define ACPI_USE_LOCAL_CACHE        1

Using ACPICA in your OS

I didn't find any good description of integrating the ACPICA source code into an operating system, and the released package is basically just a bundle of C files with little organization. This is what I ended up having to do:

  1. I copied the C files from dispatcher/, events/, executer/, hardware/, parser/, namespace/, utilities/, tables/, and resources/ into a single acpi folder.
  2. I copied the header files from include/
  3. I created my own header file based on aclinux.h where I ripped out all of the userspace stuff, then I changed around the rest to be appropriate to my OS's definitions.
  4. I edited the include/platform/acenv.h file to remove the inclusion of aclinux.h and included my header file instead.
  5. I copied over acenv.h, acgcc.h, and my header file over to my include/platform/ folder.

This is in addition to writing an AcpiOs interface layer, and it is not well indicated by the reference manual that you have to actually edit header files. Many of the macros defined in the headers are documented, though.

External links