root/src/add-ons/kernel/bus_managers/acpi/ACPICAHaiku.cpp
/******************************************************************************
 *
 * Module Name: oshaiku - Haiku OSL interfaces
 *
 *****************************************************************************/

/******************************************************************************
 *
 * 1. Copyright Notice
 *
 * Some or all of this work - Copyright (c) 1999 - 2009, Intel Corp.
 * All rights reserved.
 *
 * 2. License
 *
 * 2.1. This is your license from Intel Corp. under its intellectual property
 * rights.  You may have additional license terms from the party that provided
 * you this software, covering your right to use that party's intellectual
 * property rights.
 *
 * 2.2. Intel grants, free of charge, to any person ("Licensee") obtaining a
 * copy of the source code appearing in this file ("Covered Code") an
 * irrevocable, perpetual, worldwide license under Intel's copyrights in the
 * base code distributed originally by Intel ("Original Intel Code") to copy,
 * make derivatives, distribute, use and display any portion of the Covered
 * Code in any form, with the right to sublicense such rights; and
 *
 * 2.3. Intel grants Licensee a non-exclusive and non-transferable patent
 * license (with the right to sublicense), under only those claims of Intel
 * patents that are infringed by the Original Intel Code, to make, use, sell,
 * offer to sell, and import the Covered Code and derivative works thereof
 * solely to the minimum extent necessary to exercise the above copyright
 * license, and in no event shall the patent license extend to any additions
 * to or modifications of the Original Intel Code.  No other license or right
 * is granted directly or by implication, estoppel or otherwise;
 *
 * The above copyright and patent license is granted only if the following
 * conditions are met:
 *
 * 3. Conditions
 *
 * 3.1. Redistribution of Source with Rights to Further Distribute Source.
 * Redistribution of source code of any substantial portion of the Covered
 * Code or modification with rights to further distribute source must include
 * the above Copyright Notice, the above License, this list of Conditions,
 * and the following Disclaimer and Export Compliance provision.  In addition,
 * Licensee must cause all Covered Code to which Licensee contributes to
 * contain a file documenting the changes Licensee made to create that Covered
 * Code and the date of any change.  Licensee must include in that file the
 * documentation of any changes made by any predecessor Licensee.  Licensee
 * must include a prominent statement that the modification is derived,
 * directly or indirectly, from Original Intel Code.
 *
 * 3.2. Redistribution of Source with no Rights to Further Distribute Source.
 * Redistribution of source code of any substantial portion of the Covered
 * Code or modification without rights to further distribute source must
 * include the following Disclaimer and Export Compliance provision in the
 * documentation and/or other materials provided with distribution.  In
 * addition, Licensee may not authorize further sublicense of source of any
 * portion of the Covered Code, and must include terms to the effect that the
 * license from Licensee to its licensee is limited to the intellectual
 * property embodied in the software Licensee provides to its licensee, and
 * not to intellectual property embodied in modifications its licensee may
 * make.
 *
 * 3.3. Redistribution of Executable. Redistribution in executable form of any
 * substantial portion of the Covered Code or modification must reproduce the
 * above Copyright Notice, and the following Disclaimer and Export Compliance
 * provision in the documentation and/or other materials provided with the
 * distribution.
 *
 * 3.4. Intel retains all right, title, and interest in and to the Original
 * Intel Code.
 *
 * 3.5. Neither the name Intel nor any other trademark owned or controlled by
 * Intel shall be used in advertising or otherwise to promote the sale, use or
 * other dealings in products derived from or relating to the Covered Code
 * without prior written authorization from Intel.
 *
 * 4. Disclaimer and Export Compliance
 *
 * 4.1. INTEL MAKES NO WARRANTY OF ANY KIND REGARDING ANY SOFTWARE PROVIDED
 * HERE.  ANY SOFTWARE ORIGINATING FROM INTEL OR DERIVED FROM INTEL SOFTWARE
 * IS PROVIDED "AS IS," AND INTEL WILL NOT PROVIDE ANY SUPPORT,  ASSISTANCE,
 * INSTALLATION, TRAINING OR OTHER SERVICES.  INTEL WILL NOT PROVIDE ANY
 * UPDATES, ENHANCEMENTS OR EXTENSIONS.  INTEL SPECIFICALLY DISCLAIMS ANY
 * IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * 4.2. IN NO EVENT SHALL INTEL HAVE ANY LIABILITY TO LICENSEE, ITS LICENSEES
 * OR ANY OTHER THIRD PARTY, FOR ANY LOST PROFITS, LOST DATA, LOSS OF USE OR
 * COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
 * SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, UNDER ANY
 * CAUSE OF ACTION OR THEORY OF LIABILITY, AND IRRESPECTIVE OF WHETHER INTEL
 * HAS ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.  THESE LIMITATIONS
 * SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY
 * LIMITED REMEDY.
 *
 * 4.3. Licensee shall not export, either directly or indirectly, any of this
 * software or system incorporating such software without first obtaining any
 * required license or other approval from the U. S. Department of Commerce or
 * any other agency or department of the United States Government.  In the
 * event Licensee exports any such software from the United States or
 * re-exports any such software from a foreign destination, Licensee shall
 * ensure that the distribution and export/re-export of the software is in
 * compliance with all laws, regulations, orders, or other restrictions of the
 * U.S. Export Administration Regulations. Licensee agrees that neither it nor
 * any of its subsidiaries will export/re-export any technical data, process,
 * software, or service, directly or indirectly, to any country for which the
 * United States government or any agency thereof requires an export license,
 * other governmental approval, or letter of assurance, without first obtaining
 * such license, approval or letter.
 *
 *****************************************************************************/


#include <stdio.h>
#include <sys/cdefs.h>
#include <time.h>
#include <unistd.h>

#include <OS.h>

#ifdef _KERNEL_MODE
#       include <KernelExport.h>

#       include <dpc.h>
#       include <PCI.h>

#       include <boot_item.h>
#       include <kernel.h>
#       include <vm/vm.h>
#endif

__BEGIN_DECLS
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
#include "acparser.h"
#include "acdebug.h"
__END_DECLS

#include "arch_init.h"


ACPI_MODULE_NAME("Haiku ACPI Module")

#define _COMPONENT ACPI_OS_SERVICES

// verbosity level 0 = off, 1 = normal, 2 = all
#define DEBUG_OSHAIKU 0

#if DEBUG_OSHAIKU <= 0
// No debugging, do nothing
#       define DEBUG_FUNCTION()
#       define DEBUG_FUNCTION_F(x, y...)
#       define DEBUG_FUNCTION_V()
#       define DEBUG_FUNCTION_VF(x, y...)
#else
#       define DEBUG_FUNCTION() \
                dprintf("acpi[%" B_PRId32 "]: %s\n", find_thread(NULL), __PRETTY_FUNCTION__);
#       define DEBUG_FUNCTION_F(x, y...) \
                dprintf("acpi[%" B_PRId32 "]: %s(" x ")\n", find_thread(NULL), __PRETTY_FUNCTION__, y);
#       if DEBUG_OSHAIKU == 1
// No verbose debugging, do nothing
#               define DEBUG_FUNCTION_V()
#               define DEBUG_FUNCTION_VF(x, y...)
#       else
// Full debugging
#               define DEBUG_FUNCTION_V() \
                        dprintf("acpi[%" B_PRId32 "]: %s\n", find_thread(NULL), __PRETTY_FUNCTION__);
#               define DEBUG_FUNCTION_VF(x, y...) \
                        dprintf("acpi[%" B_PRId32 "]: %s(" x ")\n", find_thread(NULL), __PRETTY_FUNCTION__, y);
#       endif
#endif


#ifdef _KERNEL_MODE
extern pci_module_info *gPCIManager;
extern dpc_module_info *gDPC;
extern void *gDPCHandle;
#endif

extern FILE *AcpiGbl_DebugFile;
FILE *AcpiGbl_OutputFile;

static ACPI_PHYSICAL_ADDRESS sACPIRoot = 0;
static void *sInterruptHandlerData[32];


/******************************************************************************
 *
 * FUNCTION:    AcpiOsInitialize, AcpiOsTerminate
 *
 * PARAMETERS:  None
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Init and terminate.  Nothing to do.
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsInitialize()
{
#ifndef _KERNEL_MODE
        AcpiGbl_OutputFile = stdout;
#else
        AcpiGbl_OutputFile = NULL;
#endif
        DEBUG_FUNCTION();
        return AE_OK;
}


ACPI_STATUS
AcpiOsTerminate()
{
        DEBUG_FUNCTION();
        return AE_OK;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsGetRootPointer
 *
 * PARAMETERS:  None
 *
 * RETURN:      RSDP physical address
 *
 * DESCRIPTION: Gets the root pointer (RSDP)
 *
 *****************************************************************************/
ACPI_PHYSICAL_ADDRESS
AcpiOsGetRootPointer()
{
#ifdef _KERNEL_MODE
        DEBUG_FUNCTION();
        if (sACPIRoot == 0) {
                phys_addr_t* acpiRootPointer = (phys_addr_t*)get_boot_item("ACPI_ROOT_POINTER", NULL);
                if (acpiRootPointer != NULL)
                        sACPIRoot = *acpiRootPointer;
        }
        return sACPIRoot;
#else
        return AeLocalGetRootPointer();
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsPredefinedOverride
 *
 * PARAMETERS:  initVal     - Initial value of the predefined object
 *              newVal      - The new value for the object
 *
 * RETURN:      Status, pointer to value.  Null pointer returned if not
 *              overriding.
 *
 * DESCRIPTION: Allow the OS to override predefined names
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsPredefinedOverride(const ACPI_PREDEFINED_NAMES *initVal,
                ACPI_STRING *newVal)
{
        DEBUG_FUNCTION();
        if (!initVal || !newVal)
                return AE_BAD_PARAMETER;

        *newVal = NULL;
        return AE_OK;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsTableOverride
 *
 * PARAMETERS:  existingTable   - Header of current table (probably firmware)
 *              newTable        - Where an entire new table is returned.
 *
 * RETURN:      Status, pointer to new table.  Null pointer returned if no
 *              table is available to override
 *
 * DESCRIPTION: Return a different version of a table if one is available
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsTableOverride(ACPI_TABLE_HEADER *existingTable,
                ACPI_TABLE_HEADER **newTable)
{
        DEBUG_FUNCTION();
        if (!existingTable || !newTable)
                return AE_BAD_PARAMETER;

        *newTable = NULL;

#ifdef ACPI_EXEC_APP
        AeTableOverride(existingTable, newTable);
        return AE_OK;
#else
        return AE_NO_ACPI_TABLES;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsPhysicalTableOverride
 *
 * PARAMETERS:  existingTable       - Header of current table (probably firmware)
 *              newAddress          - Where new table address is returned
 *                                    (Physical address)
 *              newTableLength      - Where new table length is returned
 *
 * RETURN:      Status, address/length of new table. Null pointer returned
 *              if no table is available to override.
 *
 * DESCRIPTION: Returns AE_SUPPORT, function not used in user space.
 *
 *****************************************************************************/

ACPI_STATUS
AcpiOsPhysicalTableOverride(ACPI_TABLE_HEADER *existingTable,
        ACPI_PHYSICAL_ADDRESS *newAddress, UINT32 *newTableLength)
{
        DEBUG_FUNCTION();
    return (AE_SUPPORT);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsRedirectOutput
 *
 * PARAMETERS:  destination         - An open file handle/pointer
 *
 * RETURN:      None
 *
 * DESCRIPTION: Causes redirect of AcpiOsPrintf and AcpiOsVprintf
 *
 *****************************************************************************/
void
AcpiOsRedirectOutput(void *destination)
{
        DEBUG_FUNCTION();
        AcpiGbl_OutputFile = (FILE*)destination;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsPrintf
 *
 * PARAMETERS:  fmt, ...            Standard printf format
 *
 * RETURN:      None
 *
 * DESCRIPTION: Formatted output
 *
 *****************************************************************************/
void ACPI_INTERNAL_VAR_XFACE
AcpiOsPrintf(const char *fmt, ...)
{
        va_list args;

        DEBUG_FUNCTION();
        va_start(args, fmt);
        AcpiOsVprintf(fmt, args);
        va_end(args);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsVprintf
 *
 * PARAMETERS:  fmt                 Standard printf format
 *              args                Argument list
 *
 * RETURN:      None
 *
 * DESCRIPTION: Formatted output with argument list pointer
 *
 *****************************************************************************/
void
AcpiOsVprintf(const char *fmt, va_list args)
{
#ifndef _KERNEL_MODE
        UINT8 flags;

        flags = AcpiGbl_DbOutputFlags;
        if (flags & ACPI_DB_REDIRECTABLE_OUTPUT) {
                // Output is directable to either a file (if open) or the console
                if (AcpiGbl_DebugFile) {
                        // Output file is open, send the output there
                        vfprintf(AcpiGbl_DebugFile, fmt, args);
                } else {
                        // No redirection, send output to console (once only!)
                        flags |= ACPI_DB_CONSOLE_OUTPUT;
                }
        }

        if (flags & ACPI_DB_CONSOLE_OUTPUT) {
                vfprintf(AcpiGbl_OutputFile, fmt, args);
    }
#else
        // Buffer the output until we have a complete line to send to syslog, this avoids added
        // "KERN:" entries in the middle of the line, and mixing up of the ACPI output with other
        // messages from other CPUs
        static char outputBuffer[1024];
        
        // Append the new text to the buffer
        size_t len = strlen(outputBuffer);
        size_t printed = vsnprintf(outputBuffer + len, 1024 - len, fmt, args);
        if (printed >= 1024 - len) {
                // There was no space to fit the printed string in the outputBuffer. Remove what we added
                // there, fush the buffer, and print the long string directly
                outputBuffer[len] = '\0';
                dprintf("%s\n", outputBuffer);
                outputBuffer[0] = '\0';
                dvprintf(fmt, args);
                return;
        }

        // See if we have a complete line
        char* eol = strchr(outputBuffer + len, '\n');
        while (eol != nullptr) {
                // Print the completed line, then remove it from the buffer
                *eol = 0;
                dprintf("%s\n", outputBuffer);
                memmove(outputBuffer, eol + 1, strlen(eol + 1) + 1);
                // See if there is another line to print still in the buffer (in case ACPICA would call
                // this function with a single string containing multiple newlines)
                eol = strchr(outputBuffer, '\n');
        }
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsGetLine
 *
 * PARAMETERS:  fmt                 Standard printf format
 *              args                Argument list
 *
 * RETURN:      Actual bytes read
 *
 * DESCRIPTION: Formatted input with argument list pointer
 *
 *****************************************************************************/
UINT32
AcpiOsGetLine(char *buffer)
{
        uint32 i = 0;

#ifndef _KERNEL_MODE
        uint8 temp;

        for (i = 0; ; i++) {
                scanf("%1c", &temp);
                if (!temp || temp == '\n')
                        break;

                buffer[i] = temp;
        }
#endif

        buffer[i] = 0;
        DEBUG_FUNCTION_F("buffer: \"%s\"; result: %" B_PRIu32, buffer, i);
        return i;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsMapMemory
 *
 * PARAMETERS:  where               Physical address of memory to be mapped
 *              length              How much memory to map
 *
 * RETURN:      Pointer to mapped memory.  Null on error.
 *
 * DESCRIPTION: Map physical memory into caller's address space
 *
 *****************************************************************************/
void *
AcpiOsMapMemory(ACPI_PHYSICAL_ADDRESS where, ACPI_SIZE length)
{
#ifdef _KERNEL_MODE
        // map_physical_memory() defaults to uncached memory if no type is specified.
        // But ACPICA handles flushing caches itself, so we don't need it uncached,
        // and on some architectures (e.g. ARM) uncached memory does not support
        // unaligned accesses.
        //
        // However, if ACPICA maps (or re-maps) memory that's also used by some other
        // module (e.g. PCI configuration space), then we'll end up with the same
        // physical memory mapped twice with different attributes. On many arches,
        // this is invalid. So we stick with the default type where possible.
#if defined(__HAIKU_ARCH_ARM) || defined(__HAIKU_ARCH_ARM64)
        const uint32 memoryType = B_WRITE_BACK_MEMORY;
#else
        const uint32 memoryType = 0;
#endif

        void *there;
        area_id area = map_physical_memory("acpi_physical_mem_area", (phys_addr_t)where, length,
                B_ANY_KERNEL_ADDRESS | memoryType, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
                &there);

        DEBUG_FUNCTION_F("addr: 0x%08lx; length: %lu; mapped: %p; area: %" B_PRId32,
                (addr_t)where, (size_t)length, there, area);
        if (area < 0) {
                dprintf("ACPI: cannot map memory at 0x%" B_PRIu64 ", length %"
                        B_PRIu64 "\n", (uint64)where, (uint64)length);
                return NULL;
        }
        return there;
#else
        return NULL;
#endif

        // return ACPI_TO_POINTER((ACPI_SIZE) where);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsUnmapMemory
 *
 * PARAMETERS:  where               Logical address of memory to be unmapped
 *              length              How much memory to unmap
 *
 * RETURN:      None.
 *
 * DESCRIPTION: Delete a previously created mapping.  Where and Length must
 *              correspond to a previous mapping exactly.
 *
 *****************************************************************************/
void
AcpiOsUnmapMemory(void *where, ACPI_SIZE length)
{
        DEBUG_FUNCTION_F("mapped: %p; length: %lu", where, (size_t)length);
        delete_area(area_for(where));
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsAllocate
 *
 * PARAMETERS:  size                Amount to allocate, in bytes
 *
 * RETURN:      Pointer to the new allocation.  Null on error.
 *
 * DESCRIPTION: Allocate memory.  Algorithm is dependent on the OS.
 *
 *****************************************************************************/
void *
AcpiOsAllocate(ACPI_SIZE size)
{
        void *mem = (void *) malloc(size);
        DEBUG_FUNCTION_VF("result: %p", mem);
        return mem;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsFree
 *
 * PARAMETERS:  mem                 Pointer to previously allocated memory
 *
 * RETURN:      None.
 *
 * DESCRIPTION: Free memory allocated via AcpiOsAllocate
 *
 *****************************************************************************/
void
AcpiOsFree(void *mem)
{
        DEBUG_FUNCTION_VF("mem: %p", mem);
        free(mem);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsCreateSemaphore
 *
 * PARAMETERS:  initialUnits        - Units to be assigned to the new semaphore
 *              outHandle           - Where a handle will be returned
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Create an OS semaphore
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsCreateSemaphore(UINT32 maxUnits, UINT32 initialUnits,
                ACPI_SEMAPHORE *outHandle)
{
        if (!outHandle)
        return AE_BAD_PARAMETER;

        *outHandle = create_sem(initialUnits, "acpi_sem");
        DEBUG_FUNCTION_F("max: %" B_PRIu32 "; count: %" B_PRIu32 "; result: %" PRId32,
                (uint32)maxUnits, (uint32)initialUnits, *outHandle);

        if (*outHandle >= B_OK)
                return AE_OK;

        return *outHandle == B_BAD_VALUE ? AE_BAD_PARAMETER : AE_NO_MEMORY;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsDeleteSemaphore
 *
 * PARAMETERS:  handle              - Handle returned by AcpiOsCreateSemaphore
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Delete an OS semaphore
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsDeleteSemaphore(ACPI_SEMAPHORE handle)
{
        DEBUG_FUNCTION_F("sem: %" B_PRId32, handle);
        return delete_sem(handle) == B_OK ? AE_OK : AE_BAD_PARAMETER;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsWaitSemaphore
 *
 * PARAMETERS:  handle              - Handle returned by AcpiOsCreateSemaphore
 *              units               - How many units to wait for
 *              timeout             - How long to wait
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Wait for units
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsWaitSemaphore(ACPI_SEMAPHORE handle, UINT32 units, UINT16 timeout)
{
        ACPI_STATUS result = AE_OK;
        DEBUG_FUNCTION_VF("sem: %ld; count: %lu; timeout: %u",
                handle, (uint32)units, timeout);

        if (timeout == ACPI_WAIT_FOREVER) {
                result = acquire_sem_etc(handle, units, 0, 0)
                        == B_OK ? AE_OK : AE_BAD_PARAMETER;
        } else {
                switch (acquire_sem_etc(handle, units, B_RELATIVE_TIMEOUT,
                        (bigtime_t)timeout * 1000)) {
                        case B_OK:
                                result = AE_OK;
                                break;
                        case B_INTERRUPTED:
                        case B_TIMED_OUT:
                        case B_WOULD_BLOCK:
                                result = AE_TIME;
                                break;
                        case B_BAD_VALUE:
                        default:
                                result = AE_BAD_PARAMETER;
                                break;
                }
        }
        DEBUG_FUNCTION_VF("sem: %ld; count: %lu; timeout: %u result: %lu",
                handle, (uint32)units, timeout, (uint32)result);
        return result;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsSignalSemaphore
 *
 * PARAMETERS:  handle              - Handle returned by AcpiOsCreateSemaphore
 *              units               - Number of units to send
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Send units
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsSignalSemaphore(ACPI_SEMAPHORE handle, UINT32 units)
{
        status_t result;
        DEBUG_FUNCTION_VF("sem: %ld; count: %lu", handle, (uint32)units);
        // We can be called from interrupt handler, so don't reschedule
        result = release_sem_etc(handle, units, B_DO_NOT_RESCHEDULE);
        return result == B_OK ? AE_OK : AE_BAD_PARAMETER;
}


/******************************************************************************
 *
 * FUNCTION:    Spinlock interfaces
 *
 * DESCRIPTION: Map these interfaces to semaphore interfaces
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsCreateLock(ACPI_SPINLOCK *outHandle)
{
        *outHandle = (ACPI_SPINLOCK) malloc(sizeof(spinlock));
        DEBUG_FUNCTION_F("result: %p", *outHandle);
        if (*outHandle == NULL)
                return AE_NO_MEMORY;

        B_INITIALIZE_SPINLOCK(*outHandle);
        return AE_OK;
}


void
AcpiOsDeleteLock(ACPI_SPINLOCK handle)
{
        DEBUG_FUNCTION();
        free((void*)handle);
}


ACPI_CPU_FLAGS
AcpiOsAcquireLock(ACPI_SPINLOCK handle)
{
        cpu_status cpu;
        DEBUG_FUNCTION_F("spinlock: %p", handle);
        cpu = disable_interrupts();
        acquire_spinlock(handle);
        return cpu;
}


void
AcpiOsReleaseLock(ACPI_SPINLOCK handle, ACPI_CPU_FLAGS flags)
{
        release_spinlock(handle);
        restore_interrupts(flags);
        DEBUG_FUNCTION_F("spinlock: %p", handle);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsInstallInterruptHandler
 *
 * PARAMETERS:  interruptNumber     Level handler should respond to.
 *              Isr                 Address of the ACPI interrupt handler
 *              ExceptPtr           Where status is returned
 *
 * RETURN:      Handle to the newly installed handler.
 *
 * DESCRIPTION: Install an interrupt handler.  Used to install the ACPI
 *              OS-independent handler.
 *
 *****************************************************************************/
UINT32
AcpiOsInstallInterruptHandler(UINT32 interruptNumber,
                ACPI_OSD_HANDLER serviceRoutine, void *context)
{
        status_t result;
        DEBUG_FUNCTION_F("vector: %" B_PRIu32 "; handler: %p context %p",
                (uint32)interruptNumber, serviceRoutine, context);

#ifdef _KERNEL_MODE
        // It so happens that the Haiku and ACPI-CA interrupt handler routines
        // return the same values with the same meanings
        sInterruptHandlerData[interruptNumber] = context;
        result = install_io_interrupt_handler(interruptNumber,
                (interrupt_handler)serviceRoutine, context, 0);

        DEBUG_FUNCTION_F("vector: %" B_PRIu32 "; handler: %p context %p returned %" B_PRId32,
                (uint32)interruptNumber, serviceRoutine, context, (uint32)result);

        return result == B_OK ? AE_OK : AE_BAD_PARAMETER;
#else
        return AE_BAD_PARAMETER;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsRemoveInterruptHandler
 *
 * PARAMETERS:  Handle              Returned when handler was installed
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Uninstalls an interrupt handler.
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsRemoveInterruptHandler(UINT32 interruptNumber,
                ACPI_OSD_HANDLER serviceRoutine)
{
        DEBUG_FUNCTION_F("vector: %" B_PRIu32 "; handler: %p", (uint32)interruptNumber,
                serviceRoutine);
#ifdef _KERNEL_MODE
        return remove_io_interrupt_handler(interruptNumber,
                (interrupt_handler)serviceRoutine,
                sInterruptHandlerData[interruptNumber]) == B_OK ? AE_OK : AE_ERROR;
#else
        return AE_ERROR;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsExecute
 *
 * PARAMETERS:  type            - Type of execution
 *              function        - Address of the function to execute
 *              context         - Passed as a parameter to the function
 *
 * RETURN:      Status.
 *
 * DESCRIPTION: Execute a new thread
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsExecute(ACPI_EXECUTE_TYPE type, ACPI_OSD_EXEC_CALLBACK  function,
                void *context)
{
        DEBUG_FUNCTION();
/* TODO: Prioritize urgent?
        switch (type) {
                case OSL_GLOBAL_LOCK_HANDLER:
                case OSL_NOTIFY_HANDLER:
                case OSL_GPE_HANDLER:
                case OSL_DEBUGGER_THREAD:
                case OSL_EC_POLL_HANDLER:
                case OSL_EC_BURST_HANDLER:
                        break;
        }
*/

        if (gDPC->queue_dpc(gDPCHandle, function, context) != B_OK) {
                DEBUG_FUNCTION_F("Serious failure in AcpiOsExecute! function: %p",
                        function);
                return AE_BAD_PARAMETER;
        }
        return AE_OK;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsStall
 *
 * PARAMETERS:  microseconds        To sleep
 *
 * RETURN:      Blocks until sleep is completed.
 *
 * DESCRIPTION: Sleep at microsecond granularity
 *
 *****************************************************************************/
void
AcpiOsStall(UINT32 microseconds)
{
        DEBUG_FUNCTION_F("microseconds: %" B_PRIu32, (uint32)microseconds);
        if (microseconds)
                spin(microseconds);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsSleep
 *
 * PARAMETERS:  milliseconds        To sleep
 *
 * RETURN:      Blocks until sleep is completed.
 *
 * DESCRIPTION: Sleep at millisecond granularity
 *
 *****************************************************************************/
void
AcpiOsSleep(ACPI_INTEGER milliseconds)
{
        DEBUG_FUNCTION_F("milliseconds: %" B_PRIu32, (uint32)milliseconds);
        if (gKernelStartup)
                spin(milliseconds * 1000);
        else
                snooze(milliseconds * 1000);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsGetTimer
 *
 * PARAMETERS:  None
 *
 * RETURN:      Current time in 100 nanosecond units
 *
 * DESCRIPTION: Get the current system time
 *
 *****************************************************************************/
UINT64
AcpiOsGetTimer()
{
        DEBUG_FUNCTION();
        return system_time() * 10;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsReadPciConfiguration
 *
 * PARAMETERS:  pciId               Seg/Bus/Dev
 *              reg                 Device Register
 *              value               Buffer where value is placed
 *              width               Number of bits
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Read data from PCI configuration space
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsReadPciConfiguration(ACPI_PCI_ID *pciId, UINT32 reg, UINT64 *value,
                UINT32 width)
{
#ifdef _KERNEL_MODE
        DEBUG_FUNCTION();

        switch (width) {
                case 8:
                case 16:
                case 32:
                        *value = gPCIManager->read_pci_config(
                                pciId->Bus, pciId->Device, pciId->Function, reg, width / 8);
                        break;
                default:
                        return AE_ERROR;
        }
        return AE_OK;
#else
        return AE_ERROR;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsWritePciConfiguration
 *
 * PARAMETERS:  pciId               Seg/Bus/Dev
 *              reg                 Device Register
 *              value               Value to be written
 *              width               Number of bits
 *
 * RETURN:      Status.
 *
 * DESCRIPTION: Write data to PCI configuration space
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsWritePciConfiguration(ACPI_PCI_ID *pciId, UINT32 reg,
                ACPI_INTEGER value, UINT32 width)
{
#ifdef _KERNEL_MODE
        DEBUG_FUNCTION();
        gPCIManager->write_pci_config(
                pciId->Bus, pciId->Device, pciId->Function, reg, width / 8, value);
        return AE_OK;
#else
        return AE_ERROR;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsReadPort
 *
 * PARAMETERS:  address             Address of I/O port/register to read
 *              Value               Where value is placed
 *              width               Number of bits
 *
 * RETURN:      Value read from port
 *
 * DESCRIPTION: Read data from an I/O port or register
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsReadPort(ACPI_IO_ADDRESS address, UINT32 *value, UINT32 width)
{
#ifdef _KERNEL_MODE
        DEBUG_FUNCTION_F("addr: 0x%08lx; width: %" B_PRIu32, (addr_t)address, (uint32)width);
        switch (width) {
                case 8:
                        *value = gPCIManager->read_io_8(address);
                        break;

                case 16:
                        *value = gPCIManager->read_io_16(address);
                        break;

                case 32:
                        *value = gPCIManager->read_io_32(address);
                        break;

                default:
                        return AE_ERROR;
        }

        return AE_OK;
#else
        return AE_ERROR;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsWritePort
 *
 * PARAMETERS:  address             Address of I/O port/register to write
 *              value               Value to write
 *              width               Number of bits
 *
 * RETURN:      None
 *
 * DESCRIPTION: Write data to an I/O port or register
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsWritePort(ACPI_IO_ADDRESS address, UINT32 value, UINT32 width)
{
#ifdef _KERNEL_MODE
        DEBUG_FUNCTION_F("addr: 0x%08lx; value: %" B_PRIu32 "; width: %" B_PRIu32,
                (addr_t)address, (uint32)value, (uint32)width);
        switch (width) {
                case 8:
                        gPCIManager->write_io_8(address, value);
                        break;

                case 16:
                        gPCIManager->write_io_16(address,value);
                        break;

                case 32:
                        gPCIManager->write_io_32(address,value);
                        break;

                default:
                        return AE_ERROR;
        }

        return AE_OK;
#else
        return AE_ERROR;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsReadMemory
 *
 * PARAMETERS:  address             Physical Memory Address to read
 *              value               Where value is placed
 *              width               Number of bits
 *
 * RETURN:      Value read from physical memory address
 *
 * DESCRIPTION: Read data from a physical memory address
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsReadMemory(ACPI_PHYSICAL_ADDRESS address, UINT64 *value, UINT32 width)
{
#ifdef _KERNEL_MODE
        if (vm_memcpy_from_physical(value, (phys_addr_t)address, width / 8, false)
                != B_OK) {
                return AE_ERROR;
        }
        return AE_OK;
#else
        return AE_ERROR;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsWriteMemory
 *
 * PARAMETERS:  address             Physical Memory Address to write
 *              value               Value to write
 *              width               Number of bits
 *
 * RETURN:      None
 *
 * DESCRIPTION: Write data to a physical memory address
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsWriteMemory(ACPI_PHYSICAL_ADDRESS address, UINT64 value, UINT32 width)
{
#ifdef _KERNEL_MODE
        if (vm_memcpy_to_physical((phys_addr_t)address, &value, width / 8, false)
                        != B_OK) {
                return AE_ERROR;
        }
        return AE_OK;
#else
        return AE_ERROR;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsReadable
 *
 * PARAMETERS:  pointer             - Area to be verified
 *              length              - Size of area
 *
 * RETURN:      TRUE if readable for entire length
 *
 * DESCRIPTION: Verify that a pointer is valid for reading
 *
 *****************************************************************************/
BOOLEAN
AcpiOsReadable(void *pointer, ACPI_SIZE length)
{
#ifdef _KERNEL_MODE
        return true;
#else
        area_id id;
        area_info info;

        DEBUG_FUNCTION_F("addr: %p; length: %lu", pointer, (size_t)length);

        id = area_for(pointer);
        if (id == B_ERROR) return false;
        if (get_area_info(id, &info) != B_OK) return false;
        return (info.protection & B_READ_AREA) != 0 &&
                        ((char *)pointer) + length <= info.address + info.ram_size;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsWritable
 *
 * PARAMETERS:  pointer             - Area to be verified
 *              length              - Size of area
 *
 * RETURN:      TRUE if writable for entire length
 *
 * DESCRIPTION: Verify that a pointer is valid for writing
 *
 *****************************************************************************/
BOOLEAN
AcpiOsWritable(void *pointer, ACPI_SIZE length)
{
#ifdef _KERNEL_MODE
        return true;
#else
        area_id id;
        area_info info;

        DEBUG_FUNCTION_F("addr: %p; length: %lu", pointer, (size_t)length);

        id = area_for(pointer);
        if (id == B_ERROR) return false;
        if (get_area_info(id, &info) != B_OK) return false;
        return (info.protection & B_READ_AREA) != 0 &&
                        (info.protection & B_WRITE_AREA) != 0 &&
                        ((char *)pointer) + length <= info.address + info.ram_size;
#endif
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsGetThreadId
 *
 * PARAMETERS:  None
 *
 * RETURN:      Id of the running thread
 *
 * DESCRIPTION: Get the Id of the current (running) thread
 *
 * NOTE:        The environment header should contain this line:
 *                  #define ACPI_THREAD_ID pthread_t
 *
 *****************************************************************************/
ACPI_THREAD_ID
AcpiOsGetThreadId()
{
        thread_id thread = find_thread(NULL);
        // TODO: We arn't allowed threads with id 0, handle this case.
        // ACPI treats a 0 return as an error,
        // but we are thread 0 in early boot
        return thread;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsSignal
 *
 * PARAMETERS:  function            ACPI CA signal function code
 *              info                Pointer to function-dependent structure
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Miscellaneous functions. Example implementation only.
 *
 *****************************************************************************/
ACPI_STATUS
AcpiOsSignal(UINT32 function, void *info)
{
        DEBUG_FUNCTION();

        switch (function) {
                case ACPI_SIGNAL_FATAL:
#ifdef _KERNEL_MODE
                        panic("%s", info == NULL ? "AcpiOsSignal: fatal" : (const char*)info);
                        break;
#endif
                case ACPI_SIGNAL_BREAKPOINT:
                        if (info != NULL)
                                AcpiOsPrintf("AcpiOsBreakpoint: %s ****\n", (const char*)info);
                        else
                                AcpiOsPrintf("At AcpiOsBreakpoint ****\n");
                        break;
        }

        return AE_OK;
}


/*
 * Adapted from FreeBSD since the documentation of its intended impl
 * is lacking.
 *  Section 5.2.10.1: global lock acquire/release functions */

/*
 * Adapted from FreeBSD since the documentation of its intended impl
 * is lacking.
 * Acquire the global lock.  If busy, set the pending bit.  The caller
 * will wait for notification from the BIOS that the lock is available
 * and then attempt to acquire it again.
 */
int
AcpiOsAcquireGlobalLock(volatile uint32_t *lock)
{
        uint32_t newValue;
        uint32_t oldValue;

        do {
                oldValue = *lock;
                newValue = ((oldValue & ~ACPI_GLOCK_PENDING) | ACPI_GLOCK_OWNED);
                if ((oldValue & ACPI_GLOCK_OWNED) != 0)
                        newValue |= ACPI_GLOCK_PENDING;
        } while (atomic_test_and_set((int32*)lock, newValue, oldValue) != (int32)oldValue);

        return (newValue & ACPI_GLOCK_PENDING) == 0;
}


/*
 * Adapted from FreeBSD since the documentation of its intended impl
 * is lacking.
 * Release the global lock, returning whether there is a waiter pending.
 * If the BIOS set the pending bit, OSPM must notify the BIOS when it
 * releases the lock.
 */
int
AcpiOsReleaseGlobalLock(volatile uint32_t *lock)
{
        uint32 newValue;
        uint32 oldValue;

        do {
                oldValue = *lock;
                newValue = oldValue & ~(ACPI_GLOCK_PENDING | ACPI_GLOCK_OWNED);
        } while (atomic_test_and_set((int32*)lock, newValue, oldValue) != (int32)oldValue);

        return (oldValue & ACPI_GLOCK_PENDING) != 0;
}


ACPI_STATUS
AcpiOsCreateMutex(ACPI_MUTEX* outHandle)
{
        *outHandle = (ACPI_MUTEX) malloc(sizeof(mutex));
        DEBUG_FUNCTION_F("result: %p", *outHandle);
        if (*outHandle == NULL)
                return AE_NO_MEMORY;

        mutex_init(*outHandle, "acpi mutex");
        return AE_OK;
}


void
AcpiOsDeleteMutex(ACPI_MUTEX handle)
{
        DEBUG_FUNCTION_F("mutex: %ld", (addr_t)handle);
        mutex_destroy(handle);
        free((void*)handle);
}


ACPI_STATUS
AcpiOsAcquireMutex(ACPI_MUTEX handle, UINT16 timeout)
{
        ACPI_STATUS result = AE_OK;
        DEBUG_FUNCTION_VF("mutex: %p; timeout: %u", handle, timeout);

        if (timeout == ACPI_WAIT_FOREVER) {
                result = (mutex_lock(handle) == B_OK) ? AE_OK : AE_BAD_PARAMETER;
        } else if (timeout == ACPI_DO_NOT_WAIT) {
                result = (mutex_trylock(handle) == B_OK) ? AE_OK : AE_TIME;
        } else {
                switch (mutex_lock_with_timeout(handle, B_RELATIVE_TIMEOUT,
                        (bigtime_t)timeout * 1000)) {
                        case B_OK:
                                result = AE_OK;
                                break;
                        case B_INTERRUPTED:
                        case B_TIMED_OUT:
                        case B_WOULD_BLOCK:
                                result = AE_TIME;
                                break;
                        case B_BAD_VALUE:
                        default:
                                result = AE_BAD_PARAMETER;
                                break;
                }
        }
        DEBUG_FUNCTION_VF("mutex: %p; timeout: %u result: %lu",
                handle, timeout, (uint32)result);
        return result;
}


void
AcpiOsReleaseMutex(ACPI_MUTEX handle)
{
        DEBUG_FUNCTION_F("mutex: %p", handle);
        mutex_unlock(handle);
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsWaitEventsComplete
 *
 * PARAMETERS:  None
 *
 * RETURN:      None
 *
 * DESCRIPTION: Wait for all asynchronous events to complete. This
 *              implementation does nothing.
 *
 *****************************************************************************/
void
AcpiOsWaitEventsComplete()
{
    //TODO: FreeBSD See description.
    return;
}


/******************************************************************************
 *
 * FUNCTION:    AcpiOsEnterSleep
 *
 * PARAMETERS:  SleepState          - Which sleep state to enter
 *              RegaValue           - Register A value
 *              RegbValue           - Register B value
 *
 * RETURN:      Status
 *
 * DESCRIPTION: A hook before writing sleep registers to enter the sleep
 *              state. Return AE_CTRL_TERMINATE to skip further sleep register
 *              writes.
 *
 *****************************************************************************/

ACPI_STATUS
AcpiOsEnterSleep (
        UINT8                   SleepState,
        UINT32                  RegaValue,
        UINT32                  RegbValue)
{
        return (AE_OK);
}