ACPICA: Difference between revisions

2,489 bytes added ,  11 months ago
no edit summary
[unchecked revision][unchecked revision]
No edit summary
No edit summary
 
(33 intermediate revisions by 10 users not shown)
Line 1:
{{ACPI}}
 
The ACPI Component Architecture '''ACPICA''' provides an operating system (OS)-independent reference implementation of the [[ACPI|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.
 
Line 14 ⟶ 16:
==== AcpiOsTerminate ====
ACPI_STATUS AcpiOsTerminate()
This is called during ACPICA Shutdown (which is not the computer shutdown, just the AcpiACPI). 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 for platform compatibility. However, on x86 you can just do:
ACPI_PHYSICAL_ADDRESS AcpiOsGetRootPointer()
{
ACPI_SIZEACPI_PHYSICAL_ADDRESS Ret = 0;
AcpiFindRootPointer(&Ret);
return Ret;
}
where AcpiFindRootPointer is part of ACPICA itself.
 
Note: The ACPI specification is highly portable specification, however, it has a static part which is generally non-portable: the location of the Root System Descriptor Pointer. This pointer may be found in many different ways depending on the chipset. On PC-compatible computers (without EFI) it is located in lower memory generally somewhere between 0x80000 and 0x100000. However, even within the PC compatible platform, an EFI-enabled board will export the RSDP to the OS on when it loads it through the EFI system tables. Other boards on server machines which are not PC-compatibles, like embedded and handheld devices which implement ACPI will again, not all be expected to position the RSDP in the same place as any other board. The RSDP is therefore located in a chipset-specific manner; From the time the OS has the RSDP, the rest of ACPI is completely portable. However, the way the RSDP is found is not. This would be the reason that the ACPICA code wouldn't try to provide routines to expressly find the RSDP in a portable manner. If your system uses EFI, locate it in the system tables or use Multiboot2 compliant loader, which provides the RSDP for you.
 
==== 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 AcpiACPI 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 AcpiACPI tables. You can replace them. Just put NULL in *NewTable and return.
 
=== Memory Management ===
Line 42 ⟶ 49:
==== AcpiOsUnmapMemory ====
void AcpiOsUnmapMemory(void *where, ACPI_SIZE length)
Unmap pages mapped using AcpiOsMapMemory. whereWhere 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)
Line 65 ⟶ 73:
#define ACPI_USE_LOCAL_CACHE 1
=== Multithreading and Scheduling Services ===
To use all the features of ACPICA you need Scheduling support too. AcpicaACPICA specifies Threads but if you have only processes, that should work too. If you don't have and don't plan to have a scheduler, you can only use the Table features of ACPICA.
==== AcpiOsGetThreadId ====
ACPI_THREAD_ID AcpiOsGetThreadId()
Line 72 ⟶ 80:
==== AcpiOsExecute ====
ACPI_STATUS AcpiOsExecute(ACPI_EXECUTE_TYPE Type, ACPI_OSD_EXEC_CALLBACK Function, void *Context)
Create a new Threadthread (or process) with entry point at ''Function'' using parameter ''Context''. ''Type'' is not really useful. When the scheduler chooses this thread it has to putpass in ''Context'' onto the first argument (RDI for x86-64, stack for x86-32 (using System V ABI) to have something like:
Function(Context);
 
==== AcpiOsSleep ====
void AcpiOsSleep(UINT64 Milliseconds)
Line 80 ⟶ 89:
void AcpiOsStall(UINT32 Microseconds)
Stall the thread for ''n'' microseconds. Note: this should not put the thread in the sleep queue. The thread should keep on running. Just looping.
 
=== Mutual Exclusion and Synchronization ===
Yes, you need Spinlocks, Mutexes and Semaphores too. Nobody said it was easy. :)
==== AcpiOsCreateMutex ====
ACPI_STATUS AcpiOsCreateMutex(ACPI_MUTEX *OutHandle)
Create space for a new mutexMutex using malloc (or eventually new) and put the address of the mutexMutex in *OutHandle, return AE_NO_MEMORY if malloc or new return NULL. Else return AE_OK like in most other functions.
 
==== AcpiOsDeleteMutex ====
void AcpiOsDeleteMutex(ACPI_MUTEX Handle)
Line 91 ⟶ 102:
ACPI_STATUS AcpiOsAcquireMutex(ACPI_MUTEX Handle, UINT16 Timeout)
This would be silly too if not for the Timeout parameter. Timeout can be one of:
* 0: acquire the mutexMutex if it is free, but do not wait if it is not
* 1 - +inf: acquire the mutexMutex if it is free, but wait for ''Timeout'' milliseconds if it is not
* -1 (0xFFFF): acquire the mutexMutex if it is free, or wait until it becamesbecame free, then return
 
==== AcpiOsReleaseMutex ====
void AcpiOsReleaseMutex(ACPI_MUTEX Handle)
Line 99 ⟶ 111:
==== AcpiOsCreateSemaphore ====
ACPI_STATUS AcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_SEMAPHORE *OutHandle)
Create a new semaphoreSemaphore with the counter initialized to ''InitialUnits'' and put its address in *OutHandle. I don't know how tu use MaxUnits. The spec says: The maximum number of units this semaphoreSemaphore will be required to accept.<br> However you should be ok if you ignore this.
 
==== AcpiOsDeleteSemahore ====
==== AcpiOsDeleteSemaphore ====
ACPI_STATUS AcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle)
-_-'
 
==== AcpiOsWaitSemaphore ====
ACPI_STATUS AcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout)
Line 111 ⟶ 125:
==== AcpiOsCreateLock ====
ACPI_STATUS AcpiOsCreateLock(ACPI_SPINLOCK *OutHandle)
Create a new spinlock and put its address in *OutHandle. Spinlock should disable interrupts on the current cpuCPU to avoid scheduling and make sure that no other cpuCPU will access the reserved area.
 
==== AcpiOsDeleteLock ====
void AcpiOsDeleteLock(ACPI_HANDLE Handle)
Line 120 ⟶ 135:
void AcpiOsReleaseLock(ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags)
Release the lock. ''Flags'' is the return value of AcquireLock. If you used this to store the interrupt state, now is the moment to use it.
 
=== Interrupt Handling ===
==== AcpiOsInstallInterruptHandler ====
Line 125 ⟶ 141:
ACPI sometimes fires interrupt. ACPICA will take care of them. ''InterruptLevel'' is the IRQ number that ACPI will use. Handler is an internal function of ACPICA which handles interrupts. Context is the parameter to be past to the Handler.
If you're lucky, your IRQ manager uses handlers of this form:
uint32_t (or DWORD, or UINT32 or whatever 32-bit wide) handler(void *);
In this case just assign the handler to the irqIRQ number with that context. I wasn't as lucky so I did:
#include <Irq.h>
ACPI_OSD_HANDLER ServiceRout;
Line 146 ⟶ 162:
==== AcpiOsRemoveInterruptHandler ====
ACPI_STATUS AcpiOsRemoveInterruptHandler(UINT32 InterruptNumber, ACPI_OSD_HANDLER Handler)
Just UnregisterIrq (InterruptNumber). Handler is provided in case you have an IrqIRQ manager which can have many handlers for one IrqIRQ. This would let you know which handler on that IrqIRQ you have to remove.
 
== Using ACPICA in your OS ==
 
Line 158 ⟶ 175:
 
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.
 
=== Visual Studio experience ===
From Visual Studio, although there is little organization in the files, it is relatively easy to port. In the provided/generate directory, there is a VC 9.0 solution. The only project required for integration is "AcpiSubsystem". Copy this project along with all the files listed (you can keep the old directory structure). #define's can be used to configure certain aspects of it, and perhaps changing #ifdef WIN32 to #ifdef X86 might be a good idea (Win64 -> x64). Once this is done though the base of it is in place, and actypes.h is the only header file that needs any modification (that listed above). It might be an idea to change the option "Compile as C code" to default - it's all .c anyway. This allows you to add C++ to the project without problems. Once this is done, add OSL.c or OSLPP.cpp, write your OS layer and you are done.
 
== Code Examples ==
Here are some examples of different operations that ACPICA can help with.
 
=== Power Off ===
To power off the machine:
AcpiEnterSleepStatePrep(5);
cli(); // disable interrupts
AcpiEnterSleepState(5);
panic("power off"); // in case it didn't work!
 
== External links ==
*[httphttps://www.acpica.org/ ACPICA Website]
*[httphttps://www.acpica.org/downloadsites/acpica/files/acpica-referencereference_19.pdf ACPICA User Guide and Programmer Reference]. Explains how to integrate it in your OS. Any missing OS layer that isn't documented in this page, must be there.
 
[[Category:Power managementACPI]]
Anonymous user