// Copyright 2016 The Fuchsia Authors // // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT #include #include #include #include #include #include #include #if ARCH_X86 #include #else #error "Unsupported architecture" #endif #include "acpi.h" #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME ("oszircon") #define LOCAL_TRACE 0 /** * @brief Initialize the OSL subsystem. * * This function allows the OSL to initialize itself. It is called during * intiialization of the ACPICA subsystem. * * @return Initialization status */ ACPI_STATUS AcpiOsInitialize() { return AE_OK; } /** * @brief Terminate the OSL subsystem. * * This function allows the OSL to cleanup and terminate. It is called during * termination of the ACPICA subsystem. * * @return Termination status */ ACPI_STATUS AcpiOsTerminate() { return AE_OK; } /** * @brief Obtain the Root ACPI table pointer (RSDP). * * @return The physical address of the RSDP */ ACPI_PHYSICAL_ADDRESS AcpiOsGetRootPointer() { if (bootloader.acpi_rsdp) { return bootloader.acpi_rsdp; } else { ACPI_PHYSICAL_ADDRESS TableAddress = 0; ACPI_STATUS status = AcpiFindRootPointer(&TableAddress); if (status != AE_OK) { return 0; } return TableAddress; } } /** * @brief Allow the host OS to override a predefined ACPI object. * * @param PredefinedObject A pointer to a predefind object (name and initial * value) * @param NewValue Where a new value for the predefined object is returned. * NULL if there is no override for this object. * * @return Exception code that indicates success or reason for failure. */ ACPI_STATUS AcpiOsPredefinedOverride( const ACPI_PREDEFINED_NAMES *PredefinedObject, ACPI_STRING *NewValue) { *NewValue = NULL; return AE_OK; } /** * @brief Allow the host OS to override a firmware ACPI table via a logical * address. * * @param ExistingTable A pointer to the header of the existing ACPI table * @param NewTable Where the pointer to the replacment table is returned. The * OSL returns NULL if no replacement is provided. * * @return Exception code that indicates success or reason for failure. */ ACPI_STATUS AcpiOsTableOverride( ACPI_TABLE_HEADER *ExistingTable, ACPI_TABLE_HEADER **NewTable) { *NewTable = NULL; return AE_OK; } /** * @brief Allow the host OS to override a firmware ACPI table via a physical * address. * * @param ExistingTable A pointer to the header of the existing ACPI table * @param NewAddress Where the physical address of the replacment table is * returned. The OSL returns NULL if no replacement is provided. * @param NewLength Where the length of the replacement table is returned. * * @return Exception code that indicates success or reason for failure. */ ACPI_STATUS AcpiOsPhysicalTableOverride( ACPI_TABLE_HEADER *ExistingTable, ACPI_PHYSICAL_ADDRESS *NewAddress, UINT32 *NewTableLength) { *NewAddress = 0; return AE_OK; } /** * @brief Map physical memory into the caller's address space. * * @param PhysicalAddress A full physical address of the memory to be mapped * into the caller's address space * @param Length The amount of memory to mapped starting at the given physical * address * * @return Logical pointer to the mapped memory. A NULL pointer indicated failures. */ void *AcpiOsMapMemory( ACPI_PHYSICAL_ADDRESS PhysicalAddress, ACPI_SIZE Length) { // Caution: PhysicalAddress might not be page-aligned, Length might not // be a page multiple. ACPI_PHYSICAL_ADDRESS aligned_address = ROUNDDOWN(PhysicalAddress, PAGE_SIZE); ACPI_PHYSICAL_ADDRESS end = ROUNDUP(PhysicalAddress + Length, PAGE_SIZE); void *vaddr = NULL; zx_status_t status = VmAspace::kernel_aspace()->AllocPhysical( "acpi_mapping", end - aligned_address, &vaddr, PAGE_SIZE_SHIFT, aligned_address, 0, ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE); if (status != ZX_OK) { return NULL; } const uintptr_t real_addr = reinterpret_cast(vaddr) + (PhysicalAddress - aligned_address); return reinterpret_cast(real_addr); } /** * @brief Remove a physical to logical memory mapping. * * @param LogicalAddress The logical address that was returned from a previous * call to AcpiOsMapMemory. * @param Length The amount of memory that was mapped. This value must be * identical to the value used in the call to AcpiOsMapMemory. */ void AcpiOsUnmapMemory(void *LogicalAddress, ACPI_SIZE Length) { zx_status_t status = VmAspace::kernel_aspace()->FreeRegion( reinterpret_cast(LogicalAddress)); if (status != ZX_OK) { TRACEF("WARNING: ACPI failed to free region %p, size %" PRIu64 "\n", LogicalAddress, (uint64_t)Length); } } /** * @brief Read a value from a memory location. * * @param Address Memory address to be read. * @param Value A pointer to a location where the data is to be returned. * @param Width The memory width in bits, either 8, 16, 32, or 64. * * @return Exception code that indicates success or reason for failure. */ ACPI_STATUS AcpiOsReadMemory( ACPI_PHYSICAL_ADDRESS Address, UINT64 *Value, UINT32 Width) { PANIC_UNIMPLEMENTED; } /** * @brief Write a value to a memory location. * * @param Address Memory address where data is to be written. * @param Value Data to be written to the memory location. * @param Width The memory width in bits, either 8, 16, 32, or 64. * * @return Exception code that indicates success or reason for failure. */ ACPI_STATUS AcpiOsWriteMemory( ACPI_PHYSICAL_ADDRESS Address, UINT64 Value, UINT32 Width) { PANIC_UNIMPLEMENTED; } /** * @brief Wait for a short amount of time (fine granularity). * * Execution of the running thread is not suspended for this time. * * @param Microseconds The amount of time to delay, in microseconds. */ void AcpiOsStall(UINT32 Microseconds) { spin(Microseconds); } /** * @brief Read a value from an input port. * * @param Address Hardware I/O port address to be read. * @param Value A pointer to a location where the data is to be returned. * @param Width The port width in bits, either 8, 16, or 32. * * @return Exception code that indicates success or reason for failure. */ ACPI_STATUS AcpiOsReadPort( ACPI_IO_ADDRESS Address, UINT32 *Value, UINT32 Width) { if (Address > 0xffff) { return AE_BAD_PARAMETER; } switch (Width) { case 8: *Value = inp((uint16_t)Address); break; case 16: *Value = inpw((uint16_t)Address); break; case 32: *Value = inpd((uint16_t)Address); break; default: return AE_BAD_PARAMETER; } return AE_OK; } /** * @brief Write a value to an output port. * * @param Address Hardware I/O port address where data is to be written. * @param Value The value to be written. * @param Width The port width in bits, either 8, 16, or 32. * * @return Exception code that indicates success or reason for failure. */ ACPI_STATUS AcpiOsWritePort( ACPI_IO_ADDRESS Address, UINT32 Value, UINT32 Width) { if (Address > 0xffff) { return AE_BAD_PARAMETER; } switch (Width) { case 8: outp((uint16_t)Address, (uint8_t)Value); break; case 16: outpw((uint16_t)Address, (uint16_t)Value); break; case 32: outpd((uint16_t)Address, (uint32_t)Value); break; default: return AE_BAD_PARAMETER; } return AE_OK; } /** * @brief Allocate memory from the dynamic memory pool. * * @param Size Amount of memory to allocate. * * @return A pointer to the allocated memory. A NULL pointer is returned on * error. */ void *AcpiOsAllocate(ACPI_SIZE Size) { return malloc(Size); } /** * @brief Free previously allocated memory. * * @param Memory A pointer to the memory to be freed. */ void AcpiOsFree(void *Memory) { free(Memory); } /** * @brief Formatted stream output. * * @param Format A standard print format string. * @param ... */ void ACPI_INTERNAL_VAR_XFACE AcpiOsPrintf(const char *Format, ...) { va_list argp; va_start(argp, Format); AcpiOsVprintf(Format, argp); va_end(argp); } /** * @brief Formatted stream output. * * @param Format A standard print format string. * @param Args A variable parameter list */ void AcpiOsVprintf(const char *Format, va_list Args) { }