| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/ddk/hw/inout.h> |
| #include <lib/pci/pio.h> |
| |
| #include <acpica/acpi.h> |
| #include <bitmap/raw-bitmap.h> |
| #include <bitmap/storage.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/mutex.h> |
| |
| #include "zircon/system/ulib/acpica/osfuchsia.h" |
| |
| #ifdef __x86_64__ |
| // Essentially, we're using a bitmap here to represent each individual I/O port, so that we can |
| // keep track of which I/O ports are allowed and which are not by the kernel. |
| |
| static constexpr size_t max_io_port = UINT16_MAX; |
| static constexpr size_t io_port_bitmap_size = max_io_port + 1; |
| static fbl::Mutex bitmap_lock; |
| static bitmap::RawBitmapGeneric<bitmap::FixedStorage<io_port_bitmap_size>> port_bitmap; |
| |
| static void initialize_port_bitmap() { |
| // This cannot fail given that we're using fixed storage |
| port_bitmap.Reset(io_port_bitmap_size); |
| } |
| |
| static bool check_port_permissions(uint16_t address, uint8_t width_bytes) { |
| LTRACEF("Testing %#x until %#x, in bitmap of size %#zx\n", address, address + width_bytes, |
| port_bitmap.size()); |
| |
| return port_bitmap.Scan(address, address + width_bytes, true); |
| } |
| |
| /** |
| * @brief Make the I/O ports accessible and set them in the bitmap, so that we don't call |
| * the kernel again. |
| * |
| * @param address The I/O address. |
| * @param width_bytes The width of the access, in bytes. |
| * |
| * @return Status code that indicates success or reason for error. |
| */ |
| static zx_status_t add_port_permissions(uint16_t address, uint8_t width_bytes) { |
| zx_status_t result = port_bitmap.Set(address, address + width_bytes); |
| ZX_ASSERT(result == ZX_OK); |
| |
| LTRACEF("Adding permissions to [%#x, %#x]\n", address, address + width_bytes); |
| |
| return zx_ioports_request(ioport_resource_handle, address, width_bytes); |
| } |
| |
| /** |
| * @brief Handle all matters of I/O port permissions with the kernel. |
| * |
| * @param address The I/O address. |
| * @param width_bits The width of the access, in bits. |
| * |
| * @return Status code that indicates success or reason for error. |
| */ |
| static zx_status_t handle_port_permissions(uint16_t address, UINT32 width_bits) { |
| // It's a good idea to convert bits to bytes here, considering each |
| // I/O port "byte" has its own bit in the bitmap |
| uint8_t width_bytes = static_cast<uint8_t>(width_bits / 8); |
| |
| fbl::AutoLock g{&bitmap_lock}; |
| |
| if (!check_port_permissions(address, width_bytes)) { |
| // If the port is disallowed at the moment, call the kernel so it isn't |
| return add_port_permissions(address, width_bytes); |
| } else { |
| LTRACEF("port %#x(width %#x) was already set.\n", address, width_bytes); |
| } |
| |
| return ZX_OK; |
| } |
| |
| static ACPI_STATUS zx_status_to_acpi_status(zx_status_t st) { |
| // Note: This function was written with regard to zx_ioports_request(), |
| // but it may be a good idea to fill this out with more ZX_ statuses |
| // if needed in the future. |
| switch (st) { |
| case ZX_ERR_NO_MEMORY: |
| return AE_NO_MEMORY; |
| case ZX_ERR_ACCESS_DENIED: |
| return AE_ACCESS; |
| case ZX_ERR_INVALID_ARGS: |
| return AE_BAD_PARAMETER; |
| case ZX_OK: |
| return AE_OK; |
| default: |
| return AE_ERROR; |
| } |
| } |
| |
| /** |
| * @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 > max_io_port) { |
| return AE_BAD_PARAMETER; |
| } |
| |
| uint16_t io_port = (uint16_t)Address; |
| |
| if (zx_status_t st = handle_port_permissions(io_port, Width); st != ZX_OK) { |
| return zx_status_to_acpi_status(st); |
| } |
| |
| switch (Width) { |
| case 8: |
| *Value = inp(io_port); |
| break; |
| case 16: |
| *Value = inpw(io_port); |
| break; |
| case 32: |
| *Value = inpd(io_port); |
| 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 > max_io_port) { |
| return AE_BAD_PARAMETER; |
| } |
| |
| uint16_t io_port = (uint16_t)Address; |
| |
| if (zx_status_t st = handle_port_permissions(io_port, Width); st != ZX_OK) { |
| return zx_status_to_acpi_status(st); |
| } |
| |
| switch (Width) { |
| case 8: |
| outp(io_port, (uint8_t)Value); |
| break; |
| case 16: |
| outpw(io_port, (uint16_t)Value); |
| break; |
| case 32: |
| outpd(io_port, (uint32_t)Value); |
| break; |
| default: |
| return AE_BAD_PARAMETER; |
| } |
| return AE_OK; |
| } |
| |
| ACPI_STATUS AcpiIoPortSetup() { |
| initialize_port_bitmap(); |
| |
| // For AcpiOsWritePort and AcpiOsReadPort to operate they need access to ioports 0xCF8 and 0xCFC |
| // per the Pci Local Bus specification v3.0. Each address is a 32 bit port. |
| for (const auto addr : {kPciConfigAddrPort, kPciConfigDataPort}) { |
| zx_status_t pio_status = handle_port_permissions(addr, 32); |
| if (pio_status != ZX_OK) { |
| return zx_status_to_acpi_status(pio_status); |
| } |
| } |
| return AE_OK; |
| } |
| |
| #elif defined(__aarch64__) || defined(__riscv) |
| |
| ACPI_STATUS AcpiIoPortSetup() { return AE_OK; } |
| ACPI_STATUS AcpiOsReadPort(ACPI_IO_ADDRESS Address, UINT32* Value, UINT32 Width) { |
| return AE_NOT_IMPLEMENTED; |
| } |
| ACPI_STATUS AcpiOsWritePort(ACPI_IO_ADDRESS Address, UINT32 Value, UINT32 Width) { |
| return AE_NOT_IMPLEMENTED; |
| } |
| |
| #else |
| |
| #error handle this new architecture |
| |
| #endif |