aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/acpi/uacpi/default_handlers.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpi/uacpi/default_handlers.c')
-rw-r--r--sys/dev/acpi/uacpi/default_handlers.c336
1 files changed, 336 insertions, 0 deletions
diff --git a/sys/dev/acpi/uacpi/default_handlers.c b/sys/dev/acpi/uacpi/default_handlers.c
new file mode 100644
index 0000000..32259d6
--- /dev/null
+++ b/sys/dev/acpi/uacpi/default_handlers.c
@@ -0,0 +1,336 @@
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/helpers.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/io.h>
+#include <uacpi/kernel_api.h>
+#include <uacpi/uacpi.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#define PCI_ROOT_PNP_ID "PNP0A03"
+#define PCI_EXPRESS_ROOT_PNP_ID "PNP0A08"
+
+static uacpi_namespace_node *find_pci_root(uacpi_namespace_node *node)
+{
+ static const uacpi_char *pci_root_ids[] = {
+ PCI_ROOT_PNP_ID,
+ PCI_EXPRESS_ROOT_PNP_ID,
+ UACPI_NULL
+ };
+ uacpi_namespace_node *parent = node->parent;
+
+ while (parent != uacpi_namespace_root()) {
+ if (uacpi_device_matches_pnp_id(parent, pci_root_ids)) {
+ uacpi_trace(
+ "found a PCI root node %.4s controlling region %.4s\n",
+ parent->name.text, node->name.text
+ );
+ return parent;
+ }
+
+ parent = parent->parent;
+ }
+
+ uacpi_trace_region_error(
+ node, "unable to find PCI root controlling",
+ UACPI_STATUS_NOT_FOUND
+ );
+ return node;
+}
+
+static uacpi_status pci_region_attach(uacpi_region_attach_data *data)
+{
+ uacpi_namespace_node *node, *pci_root, *device;
+ uacpi_pci_address address = { 0 };
+ uacpi_u64 value;
+ uacpi_status ret;
+
+ node = data->region_node;
+ pci_root = find_pci_root(node);
+
+ /*
+ * Find the actual device object that is supposed to be controlling
+ * this operation region.
+ */
+ device = node;
+ while (device) {
+ uacpi_object_type type;
+
+ ret = uacpi_namespace_node_type(device, &type);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (type == UACPI_OBJECT_DEVICE)
+ break;
+
+ device = device->parent;
+ }
+
+ if (uacpi_unlikely(device == UACPI_NULL)) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ uacpi_trace_region_error(
+ node, "unable to find device responsible for", ret
+ );
+ return ret;
+ }
+
+ ret = uacpi_eval_simple_integer(device, "_ADR", &value);
+ if (ret == UACPI_STATUS_OK) {
+ address.function = (value >> 0) & 0xFF;
+ address.device = (value >> 16) & 0xFF;
+ }
+
+ ret = uacpi_eval_simple_integer(pci_root, "_SEG", &value);
+ if (ret == UACPI_STATUS_OK)
+ address.segment = value;
+
+ ret = uacpi_eval_simple_integer(pci_root, "_BBN", &value);
+ if (ret == UACPI_STATUS_OK)
+ address.bus = value;
+
+ uacpi_trace(
+ "detected PCI device %.4s@%04X:%02X:%02X:%01X\n",
+ device->name.text, address.segment, address.bus,
+ address.device, address.function
+ );
+
+ return uacpi_kernel_pci_device_open(address, &data->out_region_context);
+}
+
+static uacpi_status pci_region_detach(uacpi_region_detach_data *data)
+{
+ uacpi_kernel_pci_device_close(data->region_context);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status pci_region_do_rw(
+ uacpi_region_op op, uacpi_region_rw_data *data
+)
+{
+ uacpi_handle dev = data->region_context;
+ uacpi_u8 width;
+ uacpi_size offset;
+
+ offset = data->offset;
+ width = data->byte_width;
+
+ return op == UACPI_REGION_OP_READ ?
+ uacpi_pci_read(dev, offset, width, &data->value) :
+ uacpi_pci_write(dev, offset, width, data->value);
+}
+
+static uacpi_status handle_pci_region(uacpi_region_op op, uacpi_handle op_data)
+{
+ switch (op) {
+ case UACPI_REGION_OP_ATTACH:
+ return pci_region_attach(op_data);
+ case UACPI_REGION_OP_DETACH:
+ return pci_region_detach(op_data);
+ case UACPI_REGION_OP_READ:
+ case UACPI_REGION_OP_WRITE:
+ return pci_region_do_rw(op, op_data);
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+struct memory_region_ctx {
+ uacpi_phys_addr phys;
+ uacpi_u8 *virt;
+ uacpi_size size;
+};
+
+static uacpi_status memory_region_attach(uacpi_region_attach_data *data)
+{
+ struct memory_region_ctx *ctx;
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ ctx = uacpi_kernel_alloc(sizeof(*ctx));
+ if (ctx == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ctx->size = data->generic_info.length;
+
+ // FIXME: this really shouldn't try to map everything at once
+ ctx->phys = data->generic_info.base;
+ ctx->virt = uacpi_kernel_map(ctx->phys, ctx->size);
+
+ if (uacpi_unlikely(ctx->virt == UACPI_NULL)) {
+ ret = UACPI_STATUS_MAPPING_FAILED;
+ uacpi_trace_region_error(data->region_node, "unable to map", ret);
+ uacpi_free(ctx, sizeof(*ctx));
+ goto out;
+ }
+
+ data->out_region_context = ctx;
+out:
+ return ret;
+}
+
+static uacpi_status memory_region_detach(uacpi_region_detach_data *data)
+{
+ struct memory_region_ctx *ctx = data->region_context;
+
+ uacpi_kernel_unmap(ctx->virt, ctx->size);
+ uacpi_free(ctx, sizeof(*ctx));
+ return UACPI_STATUS_OK;
+}
+
+struct io_region_ctx {
+ uacpi_io_addr base;
+ uacpi_handle handle;
+};
+
+static uacpi_status io_region_attach(uacpi_region_attach_data *data)
+{
+ struct io_region_ctx *ctx;
+ uacpi_generic_region_info *info = &data->generic_info;
+ uacpi_status ret;
+
+ ctx = uacpi_kernel_alloc(sizeof(*ctx));
+ if (ctx == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ctx->base = info->base;
+
+ ret = uacpi_kernel_io_map(ctx->base, info->length, &ctx->handle);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_trace_region_error(
+ data->region_node, "unable to map an IO", ret
+ );
+ uacpi_free(ctx, sizeof(*ctx));
+ return ret;
+ }
+
+ data->out_region_context = ctx;
+ return ret;
+}
+
+static uacpi_status io_region_detach(uacpi_region_detach_data *data)
+{
+ struct io_region_ctx *ctx = data->region_context;
+
+ uacpi_kernel_io_unmap(ctx->handle);
+ uacpi_free(ctx, sizeof(*ctx));
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status memory_region_do_rw(
+ uacpi_region_op op, uacpi_region_rw_data *data
+)
+{
+ struct memory_region_ctx *ctx = data->region_context;
+ uacpi_size offset;
+
+ offset = data->address - ctx->phys;
+
+ return op == UACPI_REGION_OP_READ ?
+ uacpi_system_memory_read(ctx->virt, offset, data->byte_width, &data->value) :
+ uacpi_system_memory_write(ctx->virt, offset, data->byte_width, data->value);
+}
+
+static uacpi_status handle_memory_region(uacpi_region_op op, uacpi_handle op_data)
+{
+ switch (op) {
+ case UACPI_REGION_OP_ATTACH:
+ return memory_region_attach(op_data);
+ case UACPI_REGION_OP_DETACH:
+ return memory_region_detach(op_data);
+ case UACPI_REGION_OP_READ:
+ case UACPI_REGION_OP_WRITE:
+ return memory_region_do_rw(op, op_data);
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+static uacpi_status table_data_region_do_rw(
+ uacpi_region_op op, uacpi_region_rw_data *data
+)
+{
+ void *addr = UACPI_VIRT_ADDR_TO_PTR((uacpi_virt_addr)data->offset);
+
+ return op == UACPI_REGION_OP_READ ?
+ uacpi_system_memory_read(addr, 0, data->byte_width, &data->value) :
+ uacpi_system_memory_write(addr, 0, data->byte_width, data->value);
+}
+
+static uacpi_status handle_table_data_region(uacpi_region_op op, uacpi_handle op_data)
+{
+ switch (op) {
+ case UACPI_REGION_OP_ATTACH:
+ case UACPI_REGION_OP_DETACH:
+ return UACPI_STATUS_OK;
+ case UACPI_REGION_OP_READ:
+ case UACPI_REGION_OP_WRITE:
+ return table_data_region_do_rw(op, op_data);
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+static uacpi_status io_region_do_rw(
+ uacpi_region_op op, uacpi_region_rw_data *data
+)
+{
+ struct io_region_ctx *ctx = data->region_context;
+ uacpi_u8 width;
+ uacpi_size offset;
+
+ offset = data->offset - ctx->base;
+ width = data->byte_width;
+
+ return op == UACPI_REGION_OP_READ ?
+ uacpi_system_io_read(ctx->handle, offset, width, &data->value) :
+ uacpi_system_io_write(ctx->handle, offset, width, data->value);
+}
+
+static uacpi_status handle_io_region(uacpi_region_op op, uacpi_handle op_data)
+{
+ switch (op) {
+ case UACPI_REGION_OP_ATTACH:
+ return io_region_attach(op_data);
+ case UACPI_REGION_OP_DETACH:
+ return io_region_detach(op_data);
+ case UACPI_REGION_OP_READ:
+ case UACPI_REGION_OP_WRITE:
+ return io_region_do_rw(op, op_data);
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+void uacpi_install_default_address_space_handlers(void)
+{
+ uacpi_namespace_node *root;
+
+ root = uacpi_namespace_root();
+
+ uacpi_install_address_space_handler_with_flags(
+ root, UACPI_ADDRESS_SPACE_SYSTEM_MEMORY,
+ handle_memory_region, UACPI_NULL,
+ UACPI_ADDRESS_SPACE_HANDLER_DEFAULT
+ );
+
+ uacpi_install_address_space_handler_with_flags(
+ root, UACPI_ADDRESS_SPACE_SYSTEM_IO,
+ handle_io_region, UACPI_NULL,
+ UACPI_ADDRESS_SPACE_HANDLER_DEFAULT
+ );
+
+ uacpi_install_address_space_handler_with_flags(
+ root, UACPI_ADDRESS_SPACE_PCI_CONFIG,
+ handle_pci_region, UACPI_NULL,
+ UACPI_ADDRESS_SPACE_HANDLER_DEFAULT
+ );
+
+ uacpi_install_address_space_handler_with_flags(
+ root, UACPI_ADDRESS_SPACE_TABLE_DATA,
+ handle_table_data_region, UACPI_NULL,
+ UACPI_ADDRESS_SPACE_HANDLER_DEFAULT
+ );
+}
+
+#endif // !UACPI_BAREBONES_MODE