aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/acpi/uacpi/resources.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpi/uacpi/resources.c')
-rw-r--r--sys/dev/acpi/uacpi/resources.c2569
1 files changed, 2569 insertions, 0 deletions
diff --git a/sys/dev/acpi/uacpi/resources.c b/sys/dev/acpi/uacpi/resources.c
new file mode 100644
index 0000000..a9bcb82
--- /dev/null
+++ b/sys/dev/acpi/uacpi/resources.c
@@ -0,0 +1,2569 @@
+#include <uacpi/types.h>
+#include <uacpi/acpi.h>
+#include <uacpi/internal/resources.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/uacpi.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#define LARGE_RESOURCE_BASE (ACPI_RESOURCE_END_TAG + 1)
+#define L(x) (x + LARGE_RESOURCE_BASE)
+
+/*
+ * Map raw AML resource types to the internal enum, this also takes care of type
+ * sanitization by returning UACPI_AML_RESOURCE_INVALID for any unknown type.
+ */
+static const uacpi_u8 aml_resource_to_type[256] = {
+ // Small items
+ [ACPI_RESOURCE_IRQ] = UACPI_AML_RESOURCE_IRQ,
+ [ACPI_RESOURCE_DMA] = UACPI_AML_RESOURCE_DMA,
+ [ACPI_RESOURCE_START_DEPENDENT] = UACPI_AML_RESOURCE_START_DEPENDENT,
+ [ACPI_RESOURCE_END_DEPENDENT] = UACPI_AML_RESOURCE_END_DEPENDENT,
+ [ACPI_RESOURCE_IO] = UACPI_AML_RESOURCE_IO,
+ [ACPI_RESOURCE_FIXED_IO] = UACPI_AML_RESOURCE_FIXED_IO,
+ [ACPI_RESOURCE_FIXED_DMA] = UACPI_AML_RESOURCE_FIXED_DMA,
+ [ACPI_RESOURCE_VENDOR_TYPE0] = UACPI_AML_RESOURCE_VENDOR_TYPE0,
+ [ACPI_RESOURCE_END_TAG] = UACPI_AML_RESOURCE_END_TAG,
+
+ // Large items
+ [L(ACPI_RESOURCE_MEMORY24)] = UACPI_AML_RESOURCE_MEMORY24,
+ [L(ACPI_RESOURCE_GENERIC_REGISTER)] = UACPI_AML_RESOURCE_GENERIC_REGISTER,
+ [L(ACPI_RESOURCE_VENDOR_TYPE1)] = UACPI_AML_RESOURCE_VENDOR_TYPE1,
+ [L(ACPI_RESOURCE_MEMORY32)] = UACPI_AML_RESOURCE_MEMORY32,
+ [L(ACPI_RESOURCE_FIXED_MEMORY32)] = UACPI_AML_RESOURCE_FIXED_MEMORY32,
+ [L(ACPI_RESOURCE_ADDRESS32)] = UACPI_AML_RESOURCE_ADDRESS32,
+ [L(ACPI_RESOURCE_ADDRESS16)] = UACPI_AML_RESOURCE_ADDRESS16,
+ [L(ACPI_RESOURCE_EXTENDED_IRQ)] = UACPI_AML_RESOURCE_EXTENDED_IRQ,
+ [L(ACPI_RESOURCE_ADDRESS64_EXTENDED)] = UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
+ [L(ACPI_RESOURCE_ADDRESS64)] = UACPI_AML_RESOURCE_ADDRESS64,
+ [L(ACPI_RESOURCE_GPIO_CONNECTION)] = UACPI_AML_RESOURCE_GPIO_CONNECTION,
+ [L(ACPI_RESOURCE_PIN_FUNCTION)] = UACPI_AML_RESOURCE_PIN_FUNCTION,
+ [L(ACPI_RESOURCE_SERIAL_CONNECTION)] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [L(ACPI_RESOURCE_PIN_CONFIGURATION)] = UACPI_AML_RESOURCE_PIN_CONFIGURATION,
+ [L(ACPI_RESOURCE_PIN_GROUP)] = UACPI_AML_RESOURCE_PIN_GROUP,
+ [L(ACPI_RESOURCE_PIN_GROUP_FUNCTION)] = UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
+ [L(ACPI_RESOURCE_PIN_GROUP_CONFIGURATION)] = UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
+ [L(ACPI_RESOURCE_CLOCK_INPUT)] = UACPI_AML_RESOURCE_CLOCK_INPUT,
+};
+
+static const uacpi_u8 type_to_aml_resource[] = {
+ [UACPI_AML_RESOURCE_IRQ] = ACPI_RESOURCE_IRQ,
+ [UACPI_AML_RESOURCE_DMA] = ACPI_RESOURCE_DMA,
+ [UACPI_AML_RESOURCE_START_DEPENDENT] = ACPI_RESOURCE_START_DEPENDENT,
+ [UACPI_AML_RESOURCE_END_DEPENDENT] = ACPI_RESOURCE_END_DEPENDENT,
+ [UACPI_AML_RESOURCE_IO] = ACPI_RESOURCE_IO,
+ [UACPI_AML_RESOURCE_FIXED_IO] = ACPI_RESOURCE_FIXED_IO,
+ [UACPI_AML_RESOURCE_FIXED_DMA] = ACPI_RESOURCE_FIXED_DMA,
+ [UACPI_AML_RESOURCE_VENDOR_TYPE0] = ACPI_RESOURCE_VENDOR_TYPE0,
+ [UACPI_AML_RESOURCE_END_TAG] = ACPI_RESOURCE_END_TAG,
+
+ // Large items
+ [UACPI_AML_RESOURCE_MEMORY24] = ACPI_RESOURCE_MEMORY24,
+ [UACPI_AML_RESOURCE_GENERIC_REGISTER] = ACPI_RESOURCE_GENERIC_REGISTER,
+ [UACPI_AML_RESOURCE_VENDOR_TYPE1] = ACPI_RESOURCE_VENDOR_TYPE1,
+ [UACPI_AML_RESOURCE_MEMORY32] = ACPI_RESOURCE_MEMORY32,
+ [UACPI_AML_RESOURCE_FIXED_MEMORY32] = ACPI_RESOURCE_FIXED_MEMORY32,
+ [UACPI_AML_RESOURCE_ADDRESS32] = ACPI_RESOURCE_ADDRESS32,
+ [UACPI_AML_RESOURCE_ADDRESS16] = ACPI_RESOURCE_ADDRESS16,
+ [UACPI_AML_RESOURCE_EXTENDED_IRQ] = ACPI_RESOURCE_EXTENDED_IRQ,
+ [UACPI_AML_RESOURCE_ADDRESS64_EXTENDED] = ACPI_RESOURCE_ADDRESS64_EXTENDED,
+ [UACPI_AML_RESOURCE_ADDRESS64] = ACPI_RESOURCE_ADDRESS64,
+ [UACPI_AML_RESOURCE_GPIO_CONNECTION] = ACPI_RESOURCE_GPIO_CONNECTION,
+ [UACPI_AML_RESOURCE_PIN_FUNCTION] = ACPI_RESOURCE_PIN_FUNCTION,
+ [UACPI_AML_RESOURCE_SERIAL_CONNECTION] = ACPI_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_AML_RESOURCE_PIN_CONFIGURATION] = ACPI_RESOURCE_PIN_CONFIGURATION,
+ [UACPI_AML_RESOURCE_PIN_GROUP] = ACPI_RESOURCE_PIN_GROUP,
+ [UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION] = ACPI_RESOURCE_PIN_GROUP_FUNCTION,
+ [UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION] = ACPI_RESOURCE_PIN_GROUP_CONFIGURATION,
+ [UACPI_AML_RESOURCE_CLOCK_INPUT] = ACPI_RESOURCE_CLOCK_INPUT,
+};
+
+static const uacpi_u8 native_resource_to_type[UACPI_RESOURCE_TYPE_MAX + 1] = {
+ [UACPI_RESOURCE_TYPE_IRQ] = UACPI_AML_RESOURCE_IRQ,
+ [UACPI_RESOURCE_TYPE_EXTENDED_IRQ] = UACPI_AML_RESOURCE_EXTENDED_IRQ,
+ [UACPI_RESOURCE_TYPE_DMA] = UACPI_AML_RESOURCE_DMA,
+ [UACPI_RESOURCE_TYPE_FIXED_DMA] = UACPI_AML_RESOURCE_FIXED_DMA,
+ [UACPI_RESOURCE_TYPE_IO] = UACPI_AML_RESOURCE_IO,
+ [UACPI_RESOURCE_TYPE_FIXED_IO] = UACPI_AML_RESOURCE_FIXED_IO,
+ [UACPI_RESOURCE_TYPE_ADDRESS16] = UACPI_AML_RESOURCE_ADDRESS16,
+ [UACPI_RESOURCE_TYPE_ADDRESS32] = UACPI_AML_RESOURCE_ADDRESS32,
+ [UACPI_RESOURCE_TYPE_ADDRESS64] = UACPI_AML_RESOURCE_ADDRESS64,
+ [UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED] = UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
+ [UACPI_RESOURCE_TYPE_MEMORY24] = UACPI_AML_RESOURCE_MEMORY24,
+ [UACPI_RESOURCE_TYPE_MEMORY32] = UACPI_AML_RESOURCE_MEMORY32,
+ [UACPI_RESOURCE_TYPE_FIXED_MEMORY32] = UACPI_AML_RESOURCE_FIXED_MEMORY32,
+ [UACPI_RESOURCE_TYPE_START_DEPENDENT] = UACPI_AML_RESOURCE_START_DEPENDENT,
+ [UACPI_RESOURCE_TYPE_END_DEPENDENT] = UACPI_AML_RESOURCE_END_DEPENDENT,
+ [UACPI_RESOURCE_TYPE_VENDOR_SMALL] = UACPI_AML_RESOURCE_VENDOR_TYPE0,
+ [UACPI_RESOURCE_TYPE_VENDOR_LARGE] = UACPI_AML_RESOURCE_VENDOR_TYPE1,
+ [UACPI_RESOURCE_TYPE_GENERIC_REGISTER] = UACPI_AML_RESOURCE_GENERIC_REGISTER,
+ [UACPI_RESOURCE_TYPE_GPIO_CONNECTION] = UACPI_AML_RESOURCE_GPIO_CONNECTION,
+ [UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_RESOURCE_TYPE_SERIAL_SPI_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_RESOURCE_TYPE_SERIAL_UART_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_RESOURCE_TYPE_SERIAL_CSI2_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_RESOURCE_TYPE_PIN_FUNCTION] = UACPI_AML_RESOURCE_PIN_FUNCTION,
+ [UACPI_RESOURCE_TYPE_PIN_CONFIGURATION] = UACPI_AML_RESOURCE_PIN_CONFIGURATION,
+ [UACPI_RESOURCE_TYPE_PIN_GROUP] = UACPI_AML_RESOURCE_PIN_GROUP,
+ [UACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION] = UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
+ [UACPI_RESOURCE_TYPE_PIN_GROUP_CONFIGURATION] = UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
+ [UACPI_RESOURCE_TYPE_CLOCK_INPUT] = UACPI_AML_RESOURCE_CLOCK_INPUT,
+ [UACPI_RESOURCE_TYPE_END_TAG] = UACPI_AML_RESOURCE_END_TAG,
+};
+
+#define SMALL_ITEM_HEADER_SIZE sizeof(struct acpi_small_item)
+#define LARGE_ITEM_HEADER_SIZE sizeof(struct acpi_large_item)
+
+static const uacpi_u8 aml_resource_kind_to_header_size[2] = {
+ [UACPI_AML_RESOURCE_KIND_SMALL] = SMALL_ITEM_HEADER_SIZE,
+ [UACPI_AML_RESOURCE_KIND_LARGE] = LARGE_ITEM_HEADER_SIZE,
+};
+
+static uacpi_size aml_size_with_header(const struct uacpi_resource_spec *spec)
+{
+ return spec->aml_size +
+ aml_resource_kind_to_header_size[spec->resource_kind];
+}
+
+static uacpi_size extra_size_for_native_irq_or_dma(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ uacpi_u16 mask;
+ uacpi_u8 i, total_bits, num_bits = 0;
+
+ UACPI_UNUSED(size);
+
+ if (spec->type == UACPI_AML_RESOURCE_IRQ) {
+ struct acpi_resource_irq *irq = data;
+ mask = irq->irq_mask;
+ total_bits = 16;
+ } else {
+ struct acpi_resource_dma *dma = data;
+ mask = dma->channel_mask;
+ total_bits = 8;
+ }
+
+ for (i = 0; i < total_bits; ++i)
+ num_bits += !!(mask & (1 << i));
+
+ return num_bits;
+}
+
+static uacpi_size size_for_aml_irq(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_resource_irq *irq = &resource->irq;
+ uacpi_size size;
+
+ size = aml_size_with_header(spec);
+
+ switch (irq->length_kind) {
+ case UACPI_RESOURCE_LENGTH_KIND_FULL:
+ goto out_full;
+ case UACPI_RESOURCE_LENGTH_KIND_ONE_LESS:
+ case UACPI_RESOURCE_LENGTH_KIND_DONT_CARE:
+ if (irq->triggering != UACPI_TRIGGERING_EDGE)
+ goto out_full;
+ if (irq->polarity != UACPI_POLARITY_ACTIVE_HIGH)
+ goto out_full;
+ if (irq->sharing != UACPI_EXCLUSIVE)
+ goto out_full;
+
+ return size - 1;
+ }
+
+out_full:
+ if (uacpi_unlikely(irq->length_kind ==
+ UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)) {
+ uacpi_warn("requested IRQ resource length is "
+ "not compatible with specified flags, corrected\n");
+ }
+
+ return size;
+}
+
+static uacpi_size size_for_aml_start_dependent(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_resource_start_dependent *start_dep = &resource->start_dependent;
+ uacpi_size size;
+
+ size = aml_size_with_header(spec);
+ switch (start_dep->length_kind) {
+ case UACPI_RESOURCE_LENGTH_KIND_FULL:
+ goto out_full;
+ case UACPI_RESOURCE_LENGTH_KIND_ONE_LESS:
+ case UACPI_RESOURCE_LENGTH_KIND_DONT_CARE:
+ if (start_dep->compatibility != UACPI_ACCEPTABLE)
+ goto out_full;
+ if (start_dep->performance != UACPI_ACCEPTABLE)
+ goto out_full;
+
+ return size - 1;
+ }
+
+out_full:
+ if (uacpi_unlikely(start_dep->length_kind ==
+ UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)) {
+ uacpi_warn("requested StartDependentFn resource length is "
+ "not compatible with specified flags, corrected\n");
+ }
+
+ return size;
+}
+
+static uacpi_size extra_size_for_native_vendor(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ UACPI_UNUSED(spec);
+ UACPI_UNUSED(data);
+ return size;
+}
+
+static uacpi_size size_for_aml_vendor(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_size size = resource->vendor.length;
+
+ UACPI_UNUSED(spec);
+
+ if (size > 7 || resource->type == UACPI_RESOURCE_TYPE_VENDOR_LARGE) {
+ size += aml_resource_kind_to_header_size[
+ UACPI_AML_RESOURCE_KIND_LARGE
+ ];
+
+ if (uacpi_unlikely(resource->type != UACPI_RESOURCE_TYPE_VENDOR_LARGE)) {
+ uacpi_warn("vendor data too large for small descriptor (%zu), "
+ "correcting to large\n", size);
+ resource->type = UACPI_RESOURCE_TYPE_VENDOR_LARGE;
+ }
+ } else {
+ size += aml_resource_kind_to_header_size[
+ UACPI_AML_RESOURCE_KIND_SMALL
+ ];
+ }
+
+ return size;
+}
+
+static uacpi_size extra_size_for_resource_source(
+ uacpi_size base_size, uacpi_size reported_size
+)
+{
+ uacpi_size string_length;
+
+ if (reported_size <= base_size)
+ return 0;
+
+ /*
+ * The remainder of the descriptor minus the resource index field
+ */
+ string_length = (reported_size - base_size) - 1;
+ return UACPI_ALIGN_UP(string_length, sizeof(void*), uacpi_size);
+}
+
+static uacpi_size size_for_aml_resource_source(
+ uacpi_resource_source *source, uacpi_bool with_index
+)
+{
+ uacpi_size length = source->length;
+
+ if (uacpi_unlikely(length && !source->index_present)) {
+ uacpi_warn("resource declares no source index with non-empty "
+ "string (%zu bytes), corrected\n", length);
+ source->index_present = UACPI_TRUE;
+ }
+
+ // If index is included in the dynamic resource source, add it to the length
+ if (with_index)
+ length += source->index_present;
+
+ return length;
+}
+
+static uacpi_size extra_size_for_native_address_or_clock_input(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ UACPI_UNUSED(data);
+ return extra_size_for_resource_source(spec->aml_size, size);
+}
+
+static uacpi_size size_for_aml_address_or_clock_input(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_resource_source *source;
+ bool has_index = UACPI_TRUE;
+
+ switch (resource->type) {
+ case UACPI_RESOURCE_TYPE_ADDRESS16:
+ source = &resource->address16.source;
+ break;
+ case UACPI_RESOURCE_TYPE_ADDRESS32:
+ source = &resource->address32.source;
+ break;
+ case UACPI_RESOURCE_TYPE_ADDRESS64:
+ source = &resource->address64.source;
+ break;
+ case UACPI_RESOURCE_TYPE_CLOCK_INPUT:
+ source = &resource->clock_input.source;
+ has_index = UACPI_FALSE;
+ break;
+ default:
+ return 0;
+ }
+
+ return aml_size_with_header(spec) +
+ size_for_aml_resource_source(source, has_index);
+}
+
+static uacpi_size extra_size_for_extended_irq(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ struct acpi_resource_extended_irq *irq = data;
+ uacpi_size extra_size = 0;
+
+ extra_size += irq->num_irqs * sizeof(uacpi_u32);
+ extra_size += extra_size_for_resource_source(
+ spec->aml_size, size - extra_size
+ );
+
+ return extra_size;
+}
+
+static uacpi_size size_for_aml_extended_irq(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_resource_extended_irq *irq = &resource->extended_irq;
+ uacpi_size size;
+
+ size = aml_size_with_header(spec);
+ size += irq->num_irqs * 4;
+ size += size_for_aml_resource_source(&irq->source, UACPI_TRUE);
+
+ return size;
+}
+
+static uacpi_size extra_size_for_native_gpio_or_pins(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ uacpi_size pin_table_offset;
+
+ /*
+ * These resources pretend to have variable layout by declaring "offset"
+ * fields, but the layout is hardcoded and mandated by the spec to be
+ * very specific. We can use the offset numbers here to calculate the final
+ * length.
+ *
+ * For example, the layout of GPIO connection _always_ looks as follows:
+ * [0...22] -> fixed data
+ * [23...<source name offset - 1>] -> pin table
+ * [<source name offset>...<vendor data offset - 1>] -> source name
+ * [<vendor data offset>...<data offset + data length>] -> vendor data
+ */
+ switch (spec->type) {
+ case UACPI_AML_RESOURCE_GPIO_CONNECTION: {
+ struct acpi_resource_gpio_connection *gpio = data;
+ pin_table_offset = gpio->pin_table_offset;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_FUNCTION: {
+ struct acpi_resource_pin_function *pin = data;
+ pin_table_offset = pin->pin_table_offset;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_CONFIGURATION: {
+ struct acpi_resource_pin_configuration *config = data;
+ pin_table_offset = config->pin_table_offset;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_GROUP: {
+ struct acpi_resource_pin_group *group = data;
+ pin_table_offset = group->pin_table_offset;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ /*
+ * The size we get passed here does not include the header size because
+ * that's how resources are encoded. Subtract it here so that we get the
+ * correct final length.
+ */
+ return size - (pin_table_offset - LARGE_ITEM_HEADER_SIZE);
+}
+
+static uacpi_size size_for_aml_gpio_or_pins(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_size source_length, vendor_length, pin_table_length, size;
+
+ size = aml_size_with_header(spec);
+ switch (spec->type) {
+ case UACPI_AML_RESOURCE_GPIO_CONNECTION: {
+ uacpi_resource_gpio_connection *res = &resource->gpio_connection;
+ source_length = res->source.length;
+ pin_table_length = res->pin_table_length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_FUNCTION: {
+ uacpi_resource_pin_function *res = &resource->pin_function;
+ source_length = res->source.length;
+ pin_table_length = res->pin_table_length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_CONFIGURATION: {
+ uacpi_resource_pin_configuration *res = &resource->pin_configuration;
+ source_length = res->source.length;
+ pin_table_length = res->pin_table_length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_GROUP: {
+ uacpi_resource_pin_group *res = &resource->pin_group;
+ source_length = res->label.length;
+ pin_table_length = res->pin_table_length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ size += source_length;
+ size += pin_table_length * 2;
+ size += vendor_length;
+
+ return size;
+}
+
+static uacpi_size extra_size_for_native_pin_group(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ uacpi_size source_offset;
+
+ switch (spec->type) {
+ case UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION: {
+ struct acpi_resource_pin_group_function *func = data;
+ source_offset = func->source_offset;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION: {
+ struct acpi_resource_pin_group_configuration *config = data;
+ source_offset = config->source_offset;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ // Same logic as extra_size_for_native_gpio_or_pins
+ return size - (source_offset - LARGE_ITEM_HEADER_SIZE);
+}
+
+static uacpi_size size_for_aml_pin_group(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_size source_length, label_length, vendor_length, size;
+
+ size = aml_size_with_header(spec);
+ switch (spec->type) {
+ case UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION: {
+ uacpi_resource_pin_group_function *res = &resource->pin_group_function;
+ source_length = res->source.length;
+ label_length = res->label.length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION: {
+ uacpi_resource_pin_group_configuration *res;
+ res = &resource->pin_group_configuration;
+ source_length = res->source.length;
+ label_length = res->label.length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ size += source_length;
+ size += label_length;
+ size += vendor_length;
+
+ return size;
+}
+
+#define AML_SERIAL_RESOURCE_EXTRA_SIZE(type) \
+ (sizeof(struct acpi_resource_serial_##type) \
+ - sizeof(struct acpi_resource_serial))
+
+#define NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(type) \
+ (sizeof(uacpi_resource_##type##_connection) \
+ - sizeof(uacpi_resource_serial_bus_common))
+
+static const uacpi_u8 aml_serial_resource_to_extra_aml_size
+[ACPI_SERIAL_TYPE_MAX + 1] = {
+ [ACPI_SERIAL_TYPE_I2C] = AML_SERIAL_RESOURCE_EXTRA_SIZE(i2c),
+ [ACPI_SERIAL_TYPE_SPI] = AML_SERIAL_RESOURCE_EXTRA_SIZE(spi),
+ [ACPI_SERIAL_TYPE_UART] = AML_SERIAL_RESOURCE_EXTRA_SIZE(uart),
+ [ACPI_SERIAL_TYPE_CSI2] = AML_SERIAL_RESOURCE_EXTRA_SIZE(csi2),
+};
+
+static const uacpi_u8 aml_serial_resource_to_extra_native_size
+[ACPI_SERIAL_TYPE_MAX + 1] = {
+ [ACPI_SERIAL_TYPE_I2C] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(i2c),
+ [ACPI_SERIAL_TYPE_SPI] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(spi),
+ [ACPI_SERIAL_TYPE_UART] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(uart),
+ [ACPI_SERIAL_TYPE_CSI2] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(csi2),
+};
+
+static uacpi_size extra_size_for_serial_connection(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ struct acpi_resource_serial *serial = data;
+ uacpi_size extra_bytes = size;
+
+ extra_bytes -= spec->aml_size;
+ extra_bytes -= aml_serial_resource_to_extra_aml_size[serial->type];
+ extra_bytes += aml_serial_resource_to_extra_native_size[serial->type];
+
+ return extra_bytes;
+}
+
+static uacpi_size aml_size_for_serial_connection(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_size size;
+ uacpi_resource_serial_bus_common *serial_bus = &resource->serial_bus_common;
+
+ size = aml_size_with_header(spec);
+ size += aml_serial_resource_to_extra_aml_size[serial_bus->type];
+ size += serial_bus->vendor_data_length;
+ size += serial_bus->source.length;
+
+ return size;
+}
+
+#define OP(short_code, ...) \
+{ \
+ .code = UACPI_RESOURCE_CONVERT_OPCODE_##short_code, \
+ __VA_ARGS__ \
+}
+
+#define END() OP(END)
+
+#define AML_O(short_aml_name, field) \
+ uacpi_offsetof(struct acpi_resource_##short_aml_name, field)
+
+#define AML_F(short_aml_name, field) \
+ .f1.aml_offset = AML_O(short_aml_name, field)
+
+#define NATIVE_O(short_name, field) \
+ uacpi_offsetof(uacpi_resource_##short_name, field)
+
+#define NATIVE_F(short_native_name, field) \
+ .f2.native_offset = NATIVE_O(short_native_name, field)
+
+#define IMM(value) .f3.imm = value
+#define ARG0(value) .f1.arg0 = (value)
+#define ARG1(value) .f2.arg1 = (value)
+#define ARG2(value) .f3.arg2 = (value)
+
+
+static const struct uacpi_resource_convert_instruction convert_irq_to_native[] = {
+ OP(PACKED_ARRAY_16, AML_F(irq, irq_mask), NATIVE_F(irq, irqs),
+ ARG2(NATIVE_O(irq, num_irqs))),
+ OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(3), IMM(6)),
+ OP(SET_TO_IMM, NATIVE_F(irq, length_kind),
+ IMM(UACPI_RESOURCE_LENGTH_KIND_FULL)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, triggering), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, polarity), IMM(3)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, sharing), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, wake_capability), IMM(5)),
+ END(),
+ OP(SET_TO_IMM, NATIVE_F(irq, length_kind),
+ IMM(UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)),
+ OP(SET_TO_IMM, NATIVE_F(irq, triggering), IMM(UACPI_TRIGGERING_EDGE)),
+ END(),
+};
+
+const struct uacpi_resource_convert_instruction convert_irq_to_aml[] = {
+ OP(PACKED_ARRAY_16, AML_F(irq, irq_mask), NATIVE_F(irq, irqs),
+ ARG2(NATIVE_O(irq, num_irqs))),
+ OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(3), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, triggering), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, polarity), IMM(3)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, sharing), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, wake_capability), IMM(5)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_dma[] = {
+ OP(PACKED_ARRAY_8, AML_F(dma, channel_mask), NATIVE_F(dma, channels),
+ ARG2(NATIVE_O(dma, num_channels))),
+ OP(BIT_FIELD_2, AML_F(dma, flags), NATIVE_F(dma, transfer_type), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(dma, flags), NATIVE_F(dma, bus_master_status), IMM(2)),
+ OP(BIT_FIELD_2, AML_F(dma, flags), NATIVE_F(dma, channel_speed), IMM(5)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_start_dependent_to_native[] = {
+ OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(1), IMM(4)),
+ OP(SET_TO_IMM, NATIVE_F(start_dependent, length_kind),
+ IMM(UACPI_RESOURCE_LENGTH_KIND_FULL)),
+ OP(BIT_FIELD_2, AML_F(start_dependent, flags),
+ NATIVE_F(start_dependent, compatibility), IMM(0)),
+ OP(BIT_FIELD_2, AML_F(start_dependent, flags),
+ NATIVE_F(start_dependent, performance), IMM(2)),
+ END(),
+ OP(SET_TO_IMM, NATIVE_F(start_dependent, length_kind),
+ IMM(UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)),
+ OP(SET_TO_IMM, NATIVE_F(start_dependent, compatibility),
+ IMM(UACPI_ACCEPTABLE)),
+ OP(SET_TO_IMM, NATIVE_F(start_dependent, performance),
+ IMM(UACPI_ACCEPTABLE)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_start_dependent_to_aml[] = {
+ OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(1), IMM(1)),
+ OP(BIT_FIELD_2, AML_F(start_dependent, flags),
+ NATIVE_F(start_dependent, compatibility), IMM(0)),
+ OP(BIT_FIELD_2, AML_F(start_dependent, flags),
+ NATIVE_F(start_dependent, performance), IMM(2)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_io[] = {
+ OP(BIT_FIELD_1, AML_F(io, information), NATIVE_F(io, decode_type)),
+ OP(FIELD_16, AML_F(io, minimum), NATIVE_F(io, minimum)),
+ OP(FIELD_16, AML_F(io, maximum), NATIVE_F(io, maximum)),
+ OP(FIELD_8, AML_F(io, alignment), NATIVE_F(io, alignment)),
+ OP(FIELD_8, AML_F(io, length), NATIVE_F(io, length)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_fixed_io[] = {
+ OP(FIELD_16, AML_F(fixed_io, address), NATIVE_F(fixed_io, address)),
+ OP(FIELD_8, AML_F(fixed_io, length), NATIVE_F(fixed_io, length)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_fixed_dma[] = {
+ OP(FIELD_16, AML_F(fixed_dma, request_line),
+ NATIVE_F(fixed_dma, request_line)),
+ OP(FIELD_16, AML_F(fixed_dma, channel), NATIVE_F(fixed_dma, channel)),
+ OP(FIELD_8, AML_F(fixed_dma, transfer_width),
+ NATIVE_F(fixed_dma, transfer_width)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_vendor_type0[] = {
+ OP(LOAD_AML_SIZE_32, NATIVE_F(vendor, length)),
+ OP(FIELD_8, AML_F(vendor_defined_type0, byte_data), NATIVE_F(vendor, data)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_vendor_type1[] = {
+ OP(LOAD_AML_SIZE_32, NATIVE_F(vendor, length)),
+ OP(FIELD_8, AML_F(vendor_defined_type1, byte_data), NATIVE_F(vendor, data)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_memory24[] = {
+ OP(BIT_FIELD_1, AML_F(memory24, information),
+ NATIVE_F(memory24, write_status), IMM(0)),
+ OP(FIELD_16, AML_F(memory24, minimum), NATIVE_F(memory24, minimum), IMM(4)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_memory32[] = {
+ OP(BIT_FIELD_1, AML_F(memory32, information),
+ NATIVE_F(memory32, write_status), IMM(0)),
+ OP(FIELD_32, AML_F(memory32, minimum), NATIVE_F(memory32, minimum), IMM(4)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_fixed_memory32[] = {
+ OP(BIT_FIELD_1, AML_F(fixed_memory32, information),
+ NATIVE_F(fixed_memory32, write_status), IMM(0)),
+ OP(FIELD_32, AML_F(fixed_memory32, address),
+ NATIVE_F(fixed_memory32, address)),
+ OP(FIELD_32, AML_F(fixed_memory32, length),
+ NATIVE_F(fixed_memory32, length)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_generic_register[] = {
+ OP(FIELD_8, AML_F(generic_register, address_space_id),
+ NATIVE_F(generic_register, address_space_id), IMM(4)),
+ OP(FIELD_64, AML_F(generic_register, address),
+ NATIVE_F(generic_register, address)),
+ END(),
+};
+
+#define CONVERT_TYPE_SPECIFIC_FLAGS(addr_type) \
+ OP(LOAD_8_STORE, AML_F(addr_type, common.type), \
+ NATIVE_F(addr_type, common.type)), \
+ OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_RANGE_MEMORY), IMM(5)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.memory.write_status), IMM(0)), \
+ OP(BIT_FIELD_2, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.memory.caching), IMM(1)), \
+ OP(BIT_FIELD_2, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.memory.range_type), IMM(3)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.memory.translation), IMM(5)), \
+ END(), \
+ OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_RANGE_IO), IMM(4)), \
+ OP(BIT_FIELD_2, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.io.range_type), IMM(0)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.io.translation_type), IMM(4)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.io.translation), IMM(5)), \
+ END(), \
+ /* Memory type that we don't know, just copy the byte */ \
+ OP(FIELD_8, AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.type_specific), IMM(0xFF)), \
+ END()
+
+#define CONVERT_GENERAL_ADDRESS_FLAGS(addr_type) \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.flags), \
+ NATIVE_F(addr_type, common.direction), IMM(0)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.flags), \
+ NATIVE_F(addr_type, common.decode_type), IMM(1)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.flags), \
+ NATIVE_F(addr_type, common.fixed_min_address), IMM(2)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.flags), \
+ NATIVE_F(addr_type, common.fixed_max_address), IMM(3)) \
+
+#define DEFINE_ADDRESS_CONVERSION(width) \
+ static const struct uacpi_resource_convert_instruction \
+ convert_address##width[] = { \
+ CONVERT_GENERAL_ADDRESS_FLAGS(address##width), \
+ OP(FIELD_##width, AML_F(address##width, granularity), \
+ NATIVE_F(address##width, granularity), IMM(5)), \
+ OP(RESOURCE_SOURCE, NATIVE_F(address##width, source)), \
+ CONVERT_TYPE_SPECIFIC_FLAGS(address##width), \
+ };
+
+DEFINE_ADDRESS_CONVERSION(16)
+DEFINE_ADDRESS_CONVERSION(32)
+DEFINE_ADDRESS_CONVERSION(64)
+
+static const struct uacpi_resource_convert_instruction
+convert_address64_extended[] = {
+ CONVERT_GENERAL_ADDRESS_FLAGS(address64_extended),
+ OP(FIELD_8, AML_F(address64_extended, revision_id),
+ NATIVE_F(address64_extended, revision_id)),
+ OP(FIELD_64, AML_F(address64_extended, granularity),
+ NATIVE_F(address64_extended, granularity), IMM(6)),
+ CONVERT_TYPE_SPECIFIC_FLAGS(address64_extended),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_extended_irq[] = {
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, direction), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, triggering), IMM(1)),
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, polarity), IMM(2)),
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, sharing), IMM(3)),
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, wake_capability), IMM(4)),
+ OP(LOAD_8_STORE, AML_F(extended_irq, num_irqs),
+ NATIVE_F(extended_irq, num_irqs), IMM(4)),
+ OP(RESOURCE_SOURCE, NATIVE_F(extended_irq, source)),
+
+ // Use FIELD_8 here since the accumulator has been multiplied by 4
+ OP(FIELD_8, AML_F(extended_irq, irqs), NATIVE_F(extended_irq, irqs)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_clock_input[] = {
+ OP(FIELD_8, AML_F(clock_input, revision_id),
+ NATIVE_F(clock_input, revision_id)),
+ OP(BIT_FIELD_1, AML_F(clock_input, flags), NATIVE_F(clock_input, frequency),
+ IMM(0)),
+ OP(BIT_FIELD_2, AML_F(clock_input, flags), NATIVE_F(clock_input, scale),
+ IMM(1)),
+ OP(FIELD_16, AML_F(clock_input, divisor), NATIVE_F(clock_input, divisor)),
+ OP(FIELD_32, AML_F(clock_input, numerator), NATIVE_F(clock_input, numerator)),
+ OP(FIELD_8, AML_F(clock_input, source_index), NATIVE_F(clock_input, source.index)),
+ OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(clock_input, source)),
+ END(),
+};
+
+#define DECODE_SOURCE_INDEX(short_aml_name) \
+ OP(FIELD_8, AML_F(short_aml_name, source_index), \
+ NATIVE_F(short_aml_name, source.index)) \
+
+#define DECODE_RES_PIN_TBL_AND_VENDOR_DATA( \
+ short_aml_name, res_opcode, offset_field, res_field \
+) \
+ OP(LOAD_PIN_TABLE_LENGTH, AML_F(short_aml_name, offset_field), \
+ NATIVE_F(short_aml_name, pin_table_length)), \
+ OP(RESOURCE_##res_opcode, NATIVE_F(short_aml_name, res_field), \
+ AML_F(short_aml_name, offset_field), \
+ ARG2(AML_O(short_aml_name, vendor_data_offset))), \
+ OP(PIN_TABLE, AML_F(short_aml_name, pin_table_offset), \
+ NATIVE_F(short_aml_name, pin_table_length), \
+ ARG2(NATIVE_O(short_aml_name, pin_table))), \
+ OP(VENDOR_DATA, AML_F(short_aml_name, vendor_data_offset), \
+ NATIVE_F(short_aml_name, vendor_data_length), \
+ ARG2(NATIVE_O(short_aml_name, vendor_data)))
+
+static const struct uacpi_resource_convert_instruction
+convert_gpio_connection[] = {
+ OP(FIELD_8, AML_F(gpio_connection, revision_id),
+ NATIVE_F(gpio_connection, revision_id)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, general_flags),
+ NATIVE_F(gpio_connection, direction)),
+ OP(FIELD_8, AML_F(gpio_connection, pull_configuration),
+ NATIVE_F(gpio_connection, pull_configuration)),
+ OP(FIELD_16, AML_F(gpio_connection, drive_strength),
+ NATIVE_F(gpio_connection, drive_strength), IMM(2)),
+ DECODE_SOURCE_INDEX(gpio_connection),
+ DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
+ gpio_connection, SOURCE_NO_INDEX, source_offset, source
+ ),
+ OP(LOAD_8_STORE, AML_F(gpio_connection, type), NATIVE_F(gpio_connection, type)),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_GPIO_CONNECTION_INTERRUPT), IMM(5)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, intr.triggering), IMM(0)),
+ OP(BIT_FIELD_2, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, intr.polarity), IMM(1)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, intr.sharing), IMM(3)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, intr.wake_capability), IMM(4)),
+ END(),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_GPIO_CONNECTION_IO), IMM(3)),
+ OP(BIT_FIELD_2, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, io.restriction), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, io.sharing), IMM(3)),
+ END(),
+ OP(FIELD_16, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, type_specific), IMM(0xFF)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_pin_function[] = {
+ OP(FIELD_8, AML_F(pin_function, revision_id),
+ NATIVE_F(pin_function, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_function, flags),
+ NATIVE_F(pin_function, sharing), IMM(0)),
+ OP(FIELD_8, AML_F(pin_function, pull_configuration),
+ NATIVE_F(pin_function, pull_configuration)),
+ OP(FIELD_16, AML_F(pin_function, function_number),
+ NATIVE_F(pin_function, function_number)),
+ DECODE_SOURCE_INDEX(pin_function),
+ DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
+ pin_function, SOURCE_NO_INDEX, source_offset, source
+ ),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_pin_configuration[] = {
+ OP(FIELD_8, AML_F(pin_configuration, revision_id),
+ NATIVE_F(pin_configuration, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_configuration, flags),
+ NATIVE_F(pin_configuration, sharing), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(pin_configuration, flags),
+ NATIVE_F(pin_configuration, direction), IMM(1)),
+ OP(FIELD_8, AML_F(pin_configuration, type),
+ NATIVE_F(pin_configuration, type)),
+ OP(FIELD_32, AML_F(pin_configuration, value),
+ NATIVE_F(pin_configuration, value)),
+ DECODE_SOURCE_INDEX(pin_configuration),
+ DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
+ pin_configuration, SOURCE_NO_INDEX, source_offset, source
+ ),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_pin_group[] = {
+ OP(FIELD_8, AML_F(pin_group, revision_id),
+ NATIVE_F(pin_group, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_group, flags),
+ NATIVE_F(pin_group, direction), IMM(0)),
+ DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
+ pin_group, LABEL, source_lable_offset, label
+ ),
+ END(),
+};
+
+#define DECODE_PIN_GROUP_RES_SOURCES(postfix) \
+ DECODE_SOURCE_INDEX(pin_group_##postfix), \
+ OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(pin_group_##postfix, source), \
+ AML_F(pin_group_##postfix, source_offset), \
+ ARG2(AML_O(pin_group_##postfix, source_lable_offset))), \
+ OP(LOAD_16_NATIVE, NATIVE_F(pin_group_##postfix, source.length)), \
+ OP(RESOURCE_LABEL, NATIVE_F(pin_group_##postfix, label), \
+ AML_F(pin_group_##postfix, source_lable_offset), \
+ ARG2(AML_O(pin_group_##postfix, vendor_data_offset))), \
+ OP(VENDOR_DATA, AML_F(pin_group_##postfix, vendor_data_offset), \
+ NATIVE_F(pin_group_##postfix, vendor_data_length), \
+ ARG2(NATIVE_O(pin_group_##postfix, vendor_data)))
+
+static const struct uacpi_resource_convert_instruction
+convert_pin_group_function[] = {
+ OP(FIELD_8, AML_F(pin_group_function, revision_id),
+ NATIVE_F(pin_group_function, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_group_function, flags),
+ NATIVE_F(pin_group_function, sharing), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(pin_group_function, flags),
+ NATIVE_F(pin_group_function, direction), IMM(1)),
+ OP(FIELD_16, AML_F(pin_group_function, function),
+ NATIVE_F(pin_group_function, function)),
+ DECODE_PIN_GROUP_RES_SOURCES(function),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_pin_group_configuration[] = {
+ OP(FIELD_8, AML_F(pin_group_configuration, revision_id),
+ NATIVE_F(pin_group_configuration, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_group_configuration, flags),
+ NATIVE_F(pin_group_configuration, sharing), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(pin_group_configuration, flags),
+ NATIVE_F(pin_group_configuration, direction), IMM(1)),
+ OP(FIELD_8, AML_F(pin_group_configuration, type),
+ NATIVE_F(pin_group_configuration, type)),
+ OP(FIELD_32, AML_F(pin_group_configuration, value),
+ NATIVE_F(pin_group_configuration, value)),
+ DECODE_PIN_GROUP_RES_SOURCES(configuration),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_generic_serial_bus[] = {
+ OP(FIELD_8, AML_F(serial, revision_id),
+ NATIVE_F(serial_bus_common, revision_id)),
+ OP(FIELD_8, AML_F(serial, type_specific_revision_id),
+ NATIVE_F(serial_bus_common, type_revision_id)),
+ OP(FIELD_8, AML_F(serial, source_index),
+ NATIVE_F(serial_bus_common, source.index)),
+ OP(FIELD_16, AML_F(serial, type_data_length),
+ NATIVE_F(serial_bus_common, type_data_length)),
+ OP(BIT_FIELD_1, AML_F(serial, flags),
+ NATIVE_F(serial_bus_common, mode), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(serial, flags),
+ NATIVE_F(serial_bus_common, direction), IMM(1)),
+ OP(BIT_FIELD_1, AML_F(serial, flags),
+ NATIVE_F(serial_bus_common, sharing), IMM(2)),
+ OP(SERIAL_TYPE_SPECIFIC, AML_F(serial, type),
+ NATIVE_F(serial_bus_common, type)),
+ OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(serial_bus_common, source)),
+ OP(LOAD_8_NATIVE, NATIVE_F(serial_bus_common, type)),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_I2C), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
+ NATIVE_F(i2c_connection, addressing_mode), IMM(0)),
+ OP(FIELD_32, AML_F(serial_i2c, connection_speed),
+ NATIVE_F(i2c_connection, connection_speed), IMM(0xFF)),
+ OP(FIELD_16, AML_F(serial_i2c, slave_address),
+ NATIVE_F(i2c_connection, slave_address)),
+ END(),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_SPI), IMM(5)),
+ OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
+ NATIVE_F(spi_connection, wire_mode), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
+ NATIVE_F(spi_connection, device_polarity), IMM(1)),
+ OP(FIELD_32, AML_F(serial_spi, connection_speed),
+ NATIVE_F(spi_connection, connection_speed), IMM(0xFF)),
+ OP(FIELD_8, AML_F(serial_spi, data_bit_length),
+ NATIVE_F(spi_connection, data_bit_length), IMM(5)),
+ END(),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_UART), IMM(8)),
+ OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
+ NATIVE_F(uart_connection, flow_control), IMM(0)),
+ OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
+ NATIVE_F(uart_connection, stop_bits), IMM(2)),
+ OP(BIT_FIELD_3, AML_F(serial, type_specific_flags),
+ NATIVE_F(uart_connection, data_bits), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
+ NATIVE_F(uart_connection, endianness), IMM(7)),
+ OP(FIELD_32, AML_F(serial_uart, baud_rate),
+ NATIVE_F(uart_connection, baud_rate), IMM(0xFF)),
+ OP(FIELD_16, AML_F(serial_uart, rx_fifo),
+ NATIVE_F(uart_connection, rx_fifo), IMM(2)),
+ OP(FIELD_8, AML_F(serial_uart, parity),
+ NATIVE_F(uart_connection, parity), IMM(2)),
+ END(),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_CSI2), IMM(3)),
+ OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
+ NATIVE_F(csi2_connection, phy_type), IMM(0)),
+ OP(BIT_FIELD_6, AML_F(serial, type_specific_flags),
+ NATIVE_F(csi2_connection, local_port), IMM(2)),
+ END(),
+
+ /*
+ * Insert a trap to catch unimplemented types, this should be unreachable
+ * because of validation earlier.
+ */
+ OP(UNREACHABLE),
+};
+
+#define NATIVE_RESOURCE_HEADER_SIZE 8
+
+#define DEFINE_SMALL_AML_RESOURCE(aml_type_enum, native_type_enum, \
+ aml_struct, native_struct, ...) \
+ [aml_type_enum] = { \
+ .type = aml_type_enum, \
+ .native_type = native_type_enum, \
+ .resource_kind = UACPI_AML_RESOURCE_KIND_SMALL, \
+ .aml_size = sizeof(aml_struct) - SMALL_ITEM_HEADER_SIZE, \
+ .native_size = sizeof(native_struct) + NATIVE_RESOURCE_HEADER_SIZE, \
+ __VA_ARGS__ \
+ }
+
+#define DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR( \
+ aml_type_enum, native_type_enum, aml_struct, ... \
+) \
+ [aml_type_enum] = { \
+ .type = aml_type_enum, \
+ .native_type = native_type_enum, \
+ .resource_kind = UACPI_AML_RESOURCE_KIND_SMALL, \
+ .aml_size = sizeof(aml_struct) - SMALL_ITEM_HEADER_SIZE, \
+ .native_size = NATIVE_RESOURCE_HEADER_SIZE, \
+ __VA_ARGS__ \
+ }
+
+#define DEFINE_LARGE_AML_RESOURCE(aml_type_enum, native_type_enum, \
+ aml_struct, native_struct, ...) \
+ [aml_type_enum] = { \
+ .type = aml_type_enum, \
+ .native_type = native_type_enum, \
+ .resource_kind = UACPI_AML_RESOURCE_KIND_LARGE, \
+ .aml_size = sizeof(aml_struct) - LARGE_ITEM_HEADER_SIZE, \
+ .native_size = sizeof(native_struct) + NATIVE_RESOURCE_HEADER_SIZE, \
+ __VA_ARGS__ \
+ }
+
+const struct uacpi_resource_spec aml_resources[UACPI_AML_RESOURCE_MAX + 1] = {
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_IRQ,
+ UACPI_RESOURCE_TYPE_IRQ,
+ struct acpi_resource_irq,
+ uacpi_resource_irq,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS,
+ .extra_size_for_native = extra_size_for_native_irq_or_dma,
+ .size_for_aml = size_for_aml_irq,
+ .to_native = convert_irq_to_native,
+ .to_aml = convert_irq_to_aml,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_DMA,
+ UACPI_RESOURCE_TYPE_DMA,
+ struct acpi_resource_dma,
+ uacpi_resource_dma,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .extra_size_for_native = extra_size_for_native_irq_or_dma,
+ .to_native = convert_dma,
+ .to_aml = convert_dma,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_START_DEPENDENT,
+ UACPI_RESOURCE_TYPE_START_DEPENDENT,
+ struct acpi_resource_start_dependent,
+ uacpi_resource_start_dependent,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS,
+ .size_for_aml = size_for_aml_start_dependent,
+ .to_native = convert_start_dependent_to_native,
+ .to_aml = convert_start_dependent_to_aml,
+ ),
+ DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR(
+ UACPI_AML_RESOURCE_END_DEPENDENT,
+ UACPI_RESOURCE_TYPE_END_DEPENDENT,
+ struct acpi_resource_end_dependent,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_IO,
+ UACPI_RESOURCE_TYPE_IO,
+ struct acpi_resource_io,
+ uacpi_resource_io,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_io,
+ .to_aml = convert_io,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_FIXED_IO,
+ UACPI_RESOURCE_TYPE_FIXED_IO,
+ struct acpi_resource_fixed_io,
+ uacpi_resource_fixed_io,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_fixed_io,
+ .to_aml = convert_fixed_io,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_FIXED_DMA,
+ UACPI_RESOURCE_TYPE_FIXED_DMA,
+ struct acpi_resource_fixed_dma,
+ uacpi_resource_fixed_dma,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_fixed_dma,
+ .to_aml = convert_fixed_dma,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_VENDOR_TYPE0,
+ UACPI_RESOURCE_TYPE_VENDOR_SMALL,
+ struct acpi_resource_vendor_defined_type0,
+ uacpi_resource_vendor,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .size_for_aml = size_for_aml_vendor,
+ .extra_size_for_native = extra_size_for_native_vendor,
+ .to_native = convert_vendor_type0,
+ .to_aml = convert_vendor_type0,
+ ),
+ DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR(
+ UACPI_AML_RESOURCE_END_TAG,
+ UACPI_RESOURCE_TYPE_END_TAG,
+ struct acpi_resource_end_tag,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_MEMORY24,
+ UACPI_RESOURCE_TYPE_MEMORY24,
+ struct acpi_resource_memory24,
+ uacpi_resource_memory24,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_memory24,
+ .to_aml = convert_memory24,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_GENERIC_REGISTER,
+ UACPI_RESOURCE_TYPE_GENERIC_REGISTER,
+ struct acpi_resource_generic_register,
+ uacpi_resource_generic_register,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_generic_register,
+ .to_aml = convert_generic_register,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_VENDOR_TYPE1,
+ UACPI_RESOURCE_TYPE_VENDOR_LARGE,
+ struct acpi_resource_vendor_defined_type1,
+ uacpi_resource_vendor,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_vendor,
+ .size_for_aml = size_for_aml_vendor,
+ .to_native = convert_vendor_type1,
+ .to_aml = convert_vendor_type1,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_MEMORY32,
+ UACPI_RESOURCE_TYPE_MEMORY32,
+ struct acpi_resource_memory32,
+ uacpi_resource_memory32,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_memory32,
+ .to_aml = convert_memory32,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_FIXED_MEMORY32,
+ UACPI_RESOURCE_TYPE_FIXED_MEMORY32,
+ struct acpi_resource_fixed_memory32,
+ uacpi_resource_fixed_memory32,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_fixed_memory32,
+ .to_aml = convert_fixed_memory32,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_ADDRESS32,
+ UACPI_RESOURCE_TYPE_ADDRESS32,
+ struct acpi_resource_address32,
+ uacpi_resource_address32,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_address_or_clock_input,
+ .size_for_aml = size_for_aml_address_or_clock_input,
+ .to_native = convert_address32,
+ .to_aml = convert_address32,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_ADDRESS16,
+ UACPI_RESOURCE_TYPE_ADDRESS16,
+ struct acpi_resource_address16,
+ uacpi_resource_address16,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_address_or_clock_input,
+ .size_for_aml = size_for_aml_address_or_clock_input,
+ .to_native = convert_address16,
+ .to_aml = convert_address16,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_EXTENDED_IRQ,
+ UACPI_RESOURCE_TYPE_EXTENDED_IRQ,
+ struct acpi_resource_extended_irq,
+ uacpi_resource_extended_irq,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_extended_irq,
+ .size_for_aml = size_for_aml_extended_irq,
+ .to_native = convert_extended_irq,
+ .to_aml = convert_extended_irq,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_ADDRESS64,
+ UACPI_RESOURCE_TYPE_ADDRESS64,
+ struct acpi_resource_address64,
+ uacpi_resource_address64,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_address_or_clock_input,
+ .size_for_aml = size_for_aml_address_or_clock_input,
+ .to_native = convert_address64,
+ .to_aml = convert_address64,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
+ UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED,
+ struct acpi_resource_address64_extended,
+ uacpi_resource_address64_extended,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_address64_extended,
+ .to_aml = convert_address64_extended,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_GPIO_CONNECTION,
+ UACPI_RESOURCE_TYPE_GPIO_CONNECTION,
+ struct acpi_resource_gpio_connection,
+ uacpi_resource_gpio_connection,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_gpio_or_pins,
+ .size_for_aml = size_for_aml_gpio_or_pins,
+ .to_aml = convert_gpio_connection,
+ .to_native = convert_gpio_connection,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_FUNCTION,
+ UACPI_RESOURCE_TYPE_PIN_FUNCTION,
+ struct acpi_resource_pin_function,
+ uacpi_resource_pin_function,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_gpio_or_pins,
+ .size_for_aml = size_for_aml_gpio_or_pins,
+ .to_aml = convert_pin_function,
+ .to_native = convert_pin_function,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ 0, // the native type here is determined dynamically
+ struct acpi_resource_serial,
+ uacpi_resource_serial_bus_common,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_serial_connection,
+ .size_for_aml = aml_size_for_serial_connection,
+ .to_native = convert_generic_serial_bus,
+ .to_aml = convert_generic_serial_bus,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_CONFIGURATION,
+ UACPI_RESOURCE_TYPE_PIN_CONFIGURATION,
+ struct acpi_resource_pin_configuration,
+ uacpi_resource_pin_configuration,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_gpio_or_pins,
+ .size_for_aml = size_for_aml_gpio_or_pins,
+ .to_native = convert_pin_configuration,
+ .to_aml = convert_pin_configuration,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_GROUP,
+ UACPI_RESOURCE_TYPE_PIN_GROUP,
+ struct acpi_resource_pin_group,
+ uacpi_resource_pin_group,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_gpio_or_pins,
+ .size_for_aml = size_for_aml_gpio_or_pins,
+ .to_native = convert_pin_group,
+ .to_aml = convert_pin_group,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
+ UACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION,
+ struct acpi_resource_pin_group_function,
+ uacpi_resource_pin_group_function,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_pin_group,
+ .size_for_aml = size_for_aml_pin_group,
+ .to_native = convert_pin_group_function,
+ .to_aml = convert_pin_group_function,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
+ UACPI_RESOURCE_TYPE_PIN_GROUP_CONFIGURATION,
+ struct acpi_resource_pin_group_configuration,
+ uacpi_resource_pin_group_configuration,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_pin_group,
+ .size_for_aml = size_for_aml_pin_group,
+ .to_native = convert_pin_group_configuration,
+ .to_aml = convert_pin_group_configuration,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_CLOCK_INPUT,
+ UACPI_RESOURCE_TYPE_CLOCK_INPUT,
+ struct acpi_resource_clock_input,
+ uacpi_resource_clock_input,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_address_or_clock_input,
+ .size_for_aml = size_for_aml_address_or_clock_input,
+ .to_native = convert_clock_input,
+ .to_aml = convert_clock_input,
+ ),
+};
+
+static enum uacpi_aml_resource get_aml_resource_type(uacpi_u8 raw_byte)
+{
+ if (raw_byte & ACPI_LARGE_ITEM) {
+ return aml_resource_to_type[
+ LARGE_RESOURCE_BASE + (raw_byte & ACPI_LARGE_ITEM_NAME_MASK)
+ ];
+ }
+
+ return aml_resource_to_type[
+ (raw_byte >> ACPI_SMALL_ITEM_NAME_IDX) & ACPI_SMALL_ITEM_NAME_MASK
+ ];
+}
+
+static uacpi_status get_aml_resource_size(
+ uacpi_u8 *data, uacpi_size bytes_left, uacpi_u16 *out_size
+)
+{
+ uacpi_u16 size;
+
+ /*
+ * Resource header is not included in size for both, so we subtract
+ * the header size from bytes_left to validate it.
+ */
+ if (*data & ACPI_LARGE_ITEM) {
+ if (uacpi_unlikely(bytes_left < 3))
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+
+ uacpi_memcpy(&size, data + 1, sizeof(size));
+ bytes_left -= aml_resource_kind_to_header_size[
+ UACPI_AML_RESOURCE_KIND_LARGE
+ ];
+ } else {
+ size = *data & ACPI_SMALL_ITEM_LENGTH_MASK;
+ bytes_left -= aml_resource_kind_to_header_size[
+ UACPI_AML_RESOURCE_KIND_SMALL
+ ];
+ }
+
+ if (uacpi_unlikely(size > bytes_left))
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+
+ *out_size = size;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status validate_aml_serial_type(uacpi_u8 type)
+{
+ if (uacpi_unlikely(type < ACPI_SERIAL_TYPE_I2C ||
+ type > ACPI_SERIAL_TYPE_CSI2)) {
+ uacpi_error("invalid/unsupported serial connection type %d\n", type);
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_for_each_aml_resource(
+ uacpi_data_view buffer, uacpi_aml_resource_iteration_callback cb, void *user
+)
+{
+ uacpi_status ret;
+ uacpi_iteration_decision decision;
+ uacpi_u8 *data;
+ uacpi_size bytes_left;
+ uacpi_u16 resource_size;
+ enum uacpi_aml_resource type;
+ const struct uacpi_resource_spec *spec;
+
+ bytes_left = buffer.length;
+ data = buffer.bytes;
+
+ while (bytes_left) {
+ type = get_aml_resource_type(*data);
+ if (uacpi_unlikely(type == UACPI_AML_RESOURCE_TYPE_INVALID))
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+
+ ret = get_aml_resource_size(data, bytes_left, &resource_size);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ spec = &aml_resources[type];
+ switch (spec->size_kind) {
+ case UACPI_AML_RESOURCE_SIZE_KIND_FIXED:
+ if (resource_size != spec->aml_size)
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+ break;
+ case UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE:
+ if (resource_size < spec->aml_size)
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+ break;
+ case UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS:
+ if (resource_size != spec->aml_size &&
+ resource_size != (spec->aml_size - 1))
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+ break;
+ default:
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ if (spec->type == UACPI_AML_RESOURCE_SERIAL_CONNECTION) {
+ struct acpi_resource_serial *serial;
+
+ serial = (struct acpi_resource_serial*)data;
+
+ ret = validate_aml_serial_type(serial->type);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ decision = cb(user, data, resource_size, spec);
+ switch (decision) {
+ case UACPI_ITERATION_DECISION_BREAK:
+ return UACPI_STATUS_OK;
+ case UACPI_ITERATION_DECISION_CONTINUE: {
+ uacpi_size total_size = resource_size;
+
+ total_size += aml_resource_kind_to_header_size[spec->resource_kind];
+ data += total_size;
+ bytes_left -= total_size;
+ break;
+ }
+ default:
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ if (type == UACPI_AML_RESOURCE_END_TAG)
+ return UACPI_STATUS_OK;
+ }
+
+ return UACPI_STATUS_NO_RESOURCE_END_TAG;
+}
+
+static uacpi_iteration_decision find_end(
+ void *opaque, uacpi_u8 *data, uacpi_u16 resource_size,
+ const struct uacpi_resource_spec *spec
+)
+{
+ uacpi_u8 **out_ptr = opaque;
+ UACPI_UNUSED(resource_size);
+
+ if (spec->type != UACPI_AML_RESOURCE_END_TAG)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ *out_ptr = data;
+ return UACPI_ITERATION_DECISION_BREAK;
+}
+
+static uacpi_size native_size_for_aml_resource(
+ uacpi_u8 *data, uacpi_u16 size, const struct uacpi_resource_spec *spec
+)
+{
+ uacpi_size final_size = spec->native_size;
+
+ if (spec->extra_size_for_native)
+ final_size += spec->extra_size_for_native(spec, data, size);
+
+ return UACPI_ALIGN_UP(final_size, sizeof(void*), uacpi_size);
+}
+
+uacpi_status uacpi_find_aml_resource_end_tag(
+ uacpi_data_view buffer, uacpi_size *out_offset
+)
+{
+ uacpi_u8 *end_tag_ptr = UACPI_NULL;
+ uacpi_status ret;
+
+ if (buffer.length == 0) {
+ *out_offset = 0;
+ return UACPI_STATUS_OK;
+ }
+
+ /*
+ * This returning UACPI_STATUS_OK guarantees that end_tag_ptr is set to
+ * a valid value because a missing end tag would produce a
+ * UACPI_STATUS_NO_RESOURCE_END_TAG error.
+ */
+ ret = uacpi_for_each_aml_resource(buffer, find_end, &end_tag_ptr);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ *out_offset = end_tag_ptr - buffer.bytes;
+ return UACPI_STATUS_OK;
+}
+
+struct resource_conversion_ctx {
+ union {
+ void *buf;
+ uacpi_u8 *byte_buf;
+ uacpi_size size;
+ };
+ uacpi_status st;
+ uacpi_bool just_one;
+};
+
+static uacpi_iteration_decision conditional_continue(
+ struct resource_conversion_ctx *ctx
+)
+{
+ return ctx->just_one ? UACPI_ITERATION_DECISION_BREAK :
+ UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+// Opcodes that are the same for both AML->native and native->AML
+#define CONVERSION_OPCODES_COMMON(native_buf) \
+ case UACPI_RESOURCE_CONVERT_OPCODE_END: \
+ return conditional_continue(ctx); \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_8: \
+ case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_16: \
+ case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_32: \
+ case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_64: { \
+ uacpi_u8 bytes; \
+ \
+ bytes = 1 << (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_FIELD_8); \
+ accumulator = insn->f3.imm == 0xFF ? 0 : accumulator + insn->f3.imm; \
+ \
+ uacpi_memcpy(dst, src, bytes * UACPI_MAX(1, accumulator)); \
+ accumulator = 0; \
+ break; \
+ } \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_SKIP_IF_AML_SIZE_LESS_THAN: \
+ if (aml_size < insn->f1.arg0) \
+ pc += insn->f3.imm; \
+ break; \
+ case UACPI_RESOURCE_CONVERT_OPCODE_SKIP_IF_NOT_EQUALS: \
+ if (insn->f1.arg0 != accumulator) \
+ pc += insn->f3.imm; \
+ break; \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_SET_TO_IMM: \
+ uacpi_memcpy(dst, &insn->f3.imm, sizeof(insn->f3.imm)); \
+ break; \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_IMM: \
+ accumulator = insn->f3.imm; \
+ break; \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_STORE: \
+ uacpi_memcpy_zerout(&accumulator, src, sizeof(accumulator), 1); \
+ uacpi_memcpy(dst, &accumulator, 1); \
+ \
+ if (insn->f3.imm) \
+ accumulator *= insn->f3.imm; \
+ break; \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_NATIVE: \
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_16_NATIVE: { \
+ uacpi_u8 bytes; \
+ \
+ bytes = \
+ 1 << (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_NATIVE); \
+ uacpi_memcpy_zerout( \
+ &accumulator, native_buf, sizeof(accumulator), bytes \
+ ); \
+ break; \
+ } \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_UNREACHABLE: \
+ default: \
+ if (insn->code != UACPI_RESOURCE_CONVERT_OPCODE_UNREACHABLE) { \
+ uacpi_error("unhandled resource conversion opcode %d\n", \
+ insn->code); \
+ } else { \
+ uacpi_error("tried to execute unreachable conversion opcode\n"); \
+ } \
+ ctx->st = UACPI_STATUS_INTERNAL_ERROR; \
+ return UACPI_ITERATION_DECISION_BREAK;
+
+#define PTR_AT(ptr, offset) (void*)((uacpi_u8*)(ptr) + (offset))
+
+#define NATIVE_OFFSET(res, offset) \
+ PTR_AT(res, (offset) + (sizeof(uacpi_u32) * 2))
+
+#define NATIVE_FIELD(res, name, field) \
+ NATIVE_OFFSET(res, NATIVE_O(name, field))
+
+#define CHECK_AML_OOB(offset, prefix, what) \
+ if (uacpi_unlikely(offset > ((uacpi_u32)aml_size + header_size))) { \
+ uacpi_error(prefix what " is OOB: %zu > %u\n", \
+ (uacpi_size)offset, (uacpi_u32)aml_size + header_size); \
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE; \
+ return UACPI_ITERATION_DECISION_BREAK; \
+ }
+
+#define CHECK_AML_OFFSET_BASE(offset, what) \
+ if (uacpi_unlikely(offset < base_aml_size_with_header)) { \
+ uacpi_error( \
+ "invalid " what " offset: %zu, expected at least %u\n", \
+ (uacpi_size)offset, base_aml_size_with_header); \
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE; \
+ return UACPI_ITERATION_DECISION_BREAK; \
+ }
+
+#define CHECK_AML_OFFSET(offset, what) \
+ CHECK_AML_OOB(offset, "end of ", what) \
+ CHECK_AML_OFFSET_BASE(offset, what)
+
+static uacpi_resource_type aml_serial_to_native_type(
+ uacpi_u8 type
+)
+{
+ return (type - ACPI_SERIAL_TYPE_I2C) +
+ UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION;
+}
+
+static uacpi_iteration_decision do_aml_resource_to_native(
+ void *opaque, uacpi_u8 *data, uacpi_u16 aml_size,
+ const struct uacpi_resource_spec *spec
+)
+{
+ struct resource_conversion_ctx *ctx = opaque;
+ uacpi_resource *resource = ctx->buf;
+ const struct uacpi_resource_convert_instruction *insns, *insn;
+ uacpi_u8 header_size, pc = 0;
+ uacpi_u8 *src, *dst;
+ void *resource_end;
+ uacpi_u16 base_aml_size;
+ uacpi_u32 base_aml_size_with_header, accumulator = 0;
+
+ insns = spec->to_native;
+
+ header_size = aml_resource_kind_to_header_size[spec->resource_kind];
+ resource->type = spec->native_type;
+ resource->length = native_size_for_aml_resource(data, aml_size, spec);
+ resource_end = ctx->byte_buf + spec->native_size;
+ ctx->byte_buf += resource->length;
+
+ base_aml_size = base_aml_size_with_header = spec->aml_size;
+ base_aml_size_with_header += header_size;
+
+ if (insns == UACPI_NULL)
+ return conditional_continue(ctx);
+
+ for (;;) {
+ insn = &insns[pc++];
+
+ src = data + insn->f1.aml_offset;
+ dst = NATIVE_OFFSET(resource, insn->f2.native_offset);
+
+ switch (insn->code) {
+ case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_8:
+ case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16: {
+ uacpi_size i, j, max_bit;
+ uacpi_u16 value;
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16) {
+ max_bit = 16;
+ uacpi_memcpy(&value, src, sizeof(uacpi_u16));
+ } else {
+ max_bit = 8;
+ uacpi_memcpy_zerout(
+ &value, src, sizeof(value), sizeof(uacpi_u8)
+ );
+ }
+
+ for (i = 0, j = 0; i < max_bit; ++i) {
+ if (!(value & (1 << i)))
+ continue;
+
+ dst[j++] = i;
+ }
+
+ uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2), &j, 1);
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_2:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_3:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_6:{
+ uacpi_u8 mask, value;
+
+ mask = (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1) + 1;
+ mask = (1 << mask) - 1;
+
+ value = (*src >> insn->f3.imm) & mask;
+ uacpi_memcpy(dst, &value, sizeof(value));
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_AML_SIZE_32:
+ accumulator = aml_size;
+ uacpi_memcpy(dst, &accumulator, 4);
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE:
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE_NO_INDEX:
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL: {
+ uacpi_size offset = 0, max_offset, length = 0;
+ uacpi_char *src_string, *dst_string;
+ union {
+ void *ptr;
+ uacpi_resource_source *source;
+ uacpi_resource_label *label;
+ } dst_name = { 0 };
+
+ dst_name.ptr = dst;
+
+ /*
+ * Check if the string is bounded by anything at the top. If not, we
+ * just assume it ends at the end of the resource.
+ */
+ if (insn->f3.arg2) {
+ uacpi_memcpy_zerout(&max_offset, data + insn->f3.arg2,
+ sizeof(max_offset), sizeof(uacpi_u16));
+ CHECK_AML_OFFSET(max_offset, "resource source");
+ } else {
+ max_offset = aml_size + header_size;
+ }
+
+ offset += base_aml_size_with_header;
+ offset += accumulator;
+
+ if (insn->code != UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL)
+ dst_name.source->index_present = UACPI_TRUE;
+
+ if (offset >= max_offset) {
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE)
+ dst_name.source->index_present = UACPI_FALSE;
+ break;
+ }
+
+ src_string = PTR_AT(data, offset);
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE) {
+ uacpi_memcpy(&dst_name.source->index, src_string++, 1);
+ offset++;
+ }
+
+ if (offset == max_offset)
+ break;
+
+ while (offset++ < max_offset) {
+ if (src_string[length++] == '\0')
+ break;
+ }
+
+ if (src_string[length - 1] != '\0') {
+ uacpi_error("non-null-terminated resource source string\n");
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ dst_string = PTR_AT(resource_end, accumulator);
+ uacpi_memcpy(dst_string, src_string, length);
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL) {
+ dst_name.label->length = length;
+ dst_name.label->string = dst_string;
+ } else {
+ dst_name.source->length = length;
+ dst_name.source->string = dst_string;
+ }
+
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_PIN_TABLE_LENGTH:
+ uacpi_memcpy_zerout(&accumulator, src,
+ sizeof(accumulator), sizeof(uacpi_u16));
+ CHECK_AML_OFFSET(accumulator, "pin table");
+
+ accumulator -= base_aml_size_with_header;
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_PIN_TABLE: {
+ uacpi_u16 entry_count = accumulator / 2;
+
+ /*
+ * Pin table is stored right at the end of the resource buffer,
+ * copy the data there.
+ */
+ uacpi_memcpy(
+ resource_end,
+ data + base_aml_size_with_header,
+ accumulator
+ );
+
+ // Set pin_table_length
+ uacpi_memcpy(dst, &entry_count, sizeof(entry_count));
+
+ // Set pin_table pointer
+ uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2),
+ &resource_end, sizeof(void*));
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_VENDOR_DATA: {
+ uacpi_size length;
+ uacpi_u16 data_offset, offset_from_end;
+ void *native_dst, *vendor_data;
+
+ uacpi_memcpy(&data_offset, src, sizeof(data_offset));
+ CHECK_AML_OFFSET(data_offset, "vendor data");
+
+ vendor_data = data + data_offset;
+
+ /*
+ * Rebase the offset to cut off the header as it's not included
+ * in the size fields.
+ */
+ data_offset -= header_size;
+
+ length = aml_size - data_offset;
+ if (length == 0)
+ break;
+
+ uacpi_memcpy(dst, &length, sizeof(uacpi_u16));
+
+ offset_from_end = data_offset - base_aml_size;
+ native_dst = PTR_AT(resource_end, offset_from_end);
+
+ uacpi_memcpy(native_dst, vendor_data, length);
+ uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2),
+ &native_dst, sizeof(void*));
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_SERIAL_TYPE_SPECIFIC: {
+ uacpi_resource_serial_bus_common *serial_bus_common;
+ uacpi_u8 serial_type, extra_size, type_length;
+
+ serial_bus_common = &resource->serial_bus_common;
+ serial_type = *src;
+ serial_bus_common->type = serial_type;
+ resource->type = aml_serial_to_native_type(serial_type);
+
+ /*
+ * Now that we know the serial type rebase the end pointers and
+ * sizes.
+ */
+ resource_end = PTR_AT(
+ resource_end,
+ aml_serial_resource_to_extra_native_size[serial_type]
+ );
+ extra_size = aml_serial_resource_to_extra_aml_size[serial_type];
+ base_aml_size += extra_size;
+ base_aml_size_with_header += extra_size;
+
+ type_length = serial_bus_common->type_data_length;
+ if (uacpi_unlikely(type_length < extra_size)) {
+ uacpi_error(
+ "invalid type-specific data length: %d, "
+ "expected at least %d\n", type_length, extra_size
+ );
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ /*
+ * Calculate the length of the vendor data. All the extra data
+ * beyond the end of type-specific size is considered vendor data.
+ */
+ accumulator = type_length - extra_size;
+ if (accumulator == 0)
+ break;
+
+ serial_bus_common->vendor_data_length = accumulator;
+ serial_bus_common->vendor_data = resource_end;
+ uacpi_memcpy(
+ resource_end,
+ data + base_aml_size_with_header,
+ accumulator
+ );
+ break;
+ }
+
+ CONVERSION_OPCODES_COMMON(dst)
+ }
+ }
+}
+
+static uacpi_iteration_decision accumulate_native_buffer_size(
+ void *opaque, uacpi_u8 *data, uacpi_u16 resource_size,
+ const struct uacpi_resource_spec *spec
+)
+{
+ struct resource_conversion_ctx *ctx = opaque;
+ uacpi_size size_for_this;
+
+ size_for_this = native_size_for_aml_resource(data, resource_size, spec);
+ if (size_for_this == 0 || (ctx->size + size_for_this) < ctx->size) {
+ uacpi_error("invalid native size for aml resource: %zu\n",
+ size_for_this);
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ ctx->size += size_for_this;
+ return conditional_continue(ctx);
+}
+
+static uacpi_status eval_resource_helper(
+ uacpi_namespace_node *node, const uacpi_char *method,
+ uacpi_object **out_obj
+)
+{
+ uacpi_status ret;
+ uacpi_bool is_device;
+
+ ret = uacpi_namespace_node_is(node, UACPI_OBJECT_DEVICE, &is_device);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely(!is_device))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ return uacpi_eval_simple_buffer(
+ node, method, out_obj
+ );
+}
+
+uacpi_status uacpi_native_resources_from_aml(
+ uacpi_data_view aml_buffer, uacpi_resources **out_resources
+)
+{
+ uacpi_status ret;
+ struct resource_conversion_ctx ctx = { 0 };
+ uacpi_resources *resources;
+
+ ret = uacpi_for_each_aml_resource(
+ aml_buffer, accumulate_native_buffer_size, &ctx
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely_error(ctx.st))
+ return ctx.st;
+
+ // Realistically any resource buffer bigger than this is probably a bug
+ if (uacpi_unlikely(ctx.size > (5 * 1024u * 1024u))) {
+ uacpi_error("bug: bogus native resource buffer size %zu\n", ctx.size);
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ resources = uacpi_kernel_alloc_zeroed(ctx.size + sizeof(uacpi_resources));
+ if (uacpi_unlikely(resources == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ resources->length = ctx.size;
+ resources->entries = UACPI_PTR_ADD(resources, sizeof(uacpi_resources));
+
+ uacpi_memzero(&ctx, sizeof(ctx));
+ ctx.buf = resources->entries;
+
+ ret = uacpi_for_each_aml_resource(aml_buffer, do_aml_resource_to_native, &ctx);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free_resources(resources);
+ return ret;
+ }
+
+ *out_resources = resources;
+ return ret;
+}
+
+uacpi_status uacpi_get_resource_from_buffer(
+ uacpi_data_view aml_buffer, uacpi_resource **out_resource
+)
+{
+ uacpi_status ret;
+ struct resource_conversion_ctx ctx = {
+ .just_one = UACPI_TRUE,
+ };
+ uacpi_resource *resource;
+
+ ret = uacpi_for_each_aml_resource(
+ aml_buffer, accumulate_native_buffer_size, &ctx
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ resource = uacpi_kernel_alloc_zeroed(ctx.size);
+ if (uacpi_unlikely(resource == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memzero(&ctx, sizeof(ctx));
+ ctx.buf = resource;
+ ctx.just_one = UACPI_TRUE;
+
+ ret = uacpi_for_each_aml_resource(aml_buffer, do_aml_resource_to_native, &ctx);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free_resource(resource);
+ return ret;
+ }
+
+ *out_resource = resource;
+ return ret;
+}
+
+void uacpi_free_resources(uacpi_resources *resources)
+{
+ if (resources == UACPI_NULL)
+ return;
+
+ uacpi_free(resources, sizeof(uacpi_resources) + resources->length);
+}
+
+void uacpi_free_resource(uacpi_resource *resource)
+{
+ if (resource == UACPI_NULL)
+ return;
+
+ uacpi_free(resource, resource->length);
+}
+
+static uacpi_status extract_native_resources_from_method(
+ uacpi_namespace_node *device, const uacpi_char *method,
+ uacpi_resources **out_resources
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_data_view buffer;
+
+ ret = eval_resource_helper(device, method, &obj);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ uacpi_buffer_to_view(obj->buffer, &buffer);
+
+ ret = uacpi_native_resources_from_aml(buffer, out_resources);
+ uacpi_object_unref(obj);
+
+ return ret;
+}
+
+uacpi_status uacpi_get_current_resources(
+ uacpi_namespace_node *device, uacpi_resources **out_resources
+)
+{
+ return extract_native_resources_from_method(device, "_CRS", out_resources);
+}
+
+uacpi_status uacpi_get_possible_resources(
+ uacpi_namespace_node *device, uacpi_resources **out_resources
+)
+{
+ return extract_native_resources_from_method(device, "_PRS", out_resources);
+}
+
+uacpi_status uacpi_get_device_resources(
+ uacpi_namespace_node *device, const uacpi_char *method,
+ uacpi_resources **out_resources
+)
+{
+ return extract_native_resources_from_method(device, method, out_resources);
+}
+
+uacpi_status uacpi_for_each_resource(
+ uacpi_resources *resources, uacpi_resource_iteration_callback cb, void *user
+)
+{
+ uacpi_size bytes_left = resources->length;
+ uacpi_resource *current = resources->entries;
+ uacpi_iteration_decision decision;
+
+ while (bytes_left) {
+ // At least the head bytes
+ if (uacpi_unlikely(bytes_left < 4)) {
+ uacpi_error("corrupted resource buffer %p length %zu\n",
+ resources, resources->length);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_unlikely(current->type > UACPI_RESOURCE_TYPE_MAX)) {
+ uacpi_error("invalid resource type %d\n", current->type);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_unlikely(current->length > bytes_left)) {
+ uacpi_error("corrupted resource@%p length %u (%zu bytes left)\n",
+ current, current->length, bytes_left);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ decision = cb(user, current);
+
+ if (decision == UACPI_ITERATION_DECISION_BREAK ||
+ current->type == UACPI_RESOURCE_TYPE_END_TAG)
+ return UACPI_STATUS_OK;
+
+ bytes_left -= current->length;
+ current = (uacpi_resource*)((uacpi_u8*)current + current->length);
+ }
+
+ return UACPI_STATUS_NO_RESOURCE_END_TAG;
+}
+
+uacpi_status uacpi_for_each_device_resource(
+ uacpi_namespace_node *device, const uacpi_char *method,
+ uacpi_resource_iteration_callback cb, void *user
+)
+{
+ uacpi_status ret;
+ uacpi_resources *resources;
+
+ ret = extract_native_resources_from_method(device, method, &resources);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_for_each_resource(resources, cb, user);
+ uacpi_free_resources(resources);
+
+ return ret;
+}
+
+static const struct uacpi_resource_spec *resource_spec_from_native(
+ uacpi_resource *resource
+)
+{
+ return &aml_resources[native_resource_to_type[resource->type]];
+}
+
+static uacpi_size aml_size_for_native_resource(
+ uacpi_resource *resource, const struct uacpi_resource_spec *spec
+)
+{
+ return spec->size_for_aml ?
+ spec->size_for_aml(spec, resource) :
+ aml_size_with_header(spec);
+}
+
+static uacpi_iteration_decision do_native_resource_to_aml(
+ void *opaque, uacpi_resource *resource
+)
+{
+ struct resource_conversion_ctx *ctx = opaque;
+ const struct uacpi_resource_spec *spec;
+ const struct uacpi_resource_convert_instruction *insns, *insn;
+ uacpi_u8 pc = 0;
+ uacpi_u8 *dst_base, *src, *dst;
+ uacpi_u32 aml_size, base_aml_size_with_header, accumulator = 0;
+ void *resource_end;
+
+ spec = resource_spec_from_native(resource);
+ aml_size = aml_size_for_native_resource(resource, spec);
+ insns = spec->to_aml;
+
+ dst_base = ctx->byte_buf;
+ ctx->byte_buf += aml_size;
+ aml_size -= aml_resource_kind_to_header_size[spec->resource_kind];
+
+ base_aml_size_with_header = spec->aml_size;
+ base_aml_size_with_header += aml_resource_kind_to_header_size[
+ spec->resource_kind
+ ];
+ resource_end = PTR_AT(resource, spec->native_size);
+
+ if (spec->resource_kind == UACPI_AML_RESOURCE_KIND_LARGE) {
+ *dst_base = ACPI_LARGE_ITEM | type_to_aml_resource[spec->type];
+ uacpi_memcpy(dst_base + 1, &aml_size, sizeof(uacpi_u16));
+ } else {
+ *dst_base = type_to_aml_resource[spec->type] << ACPI_SMALL_ITEM_NAME_IDX;
+ *dst_base |= aml_size;
+ }
+
+ if (insns == UACPI_NULL)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ for (;;) {
+ insn = &insns[pc++];
+
+ src = NATIVE_OFFSET(resource, insn->f2.native_offset);
+ dst = dst_base + insn->f1.aml_offset;
+
+ switch (insn->code) {
+ case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_8:
+ case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16: {
+ uacpi_u8 i, *array_size, bytes = 1;
+ uacpi_u16 mask = 0;
+
+ array_size = NATIVE_OFFSET(resource, insn->f3.arg2);
+ for (i = 0; i < *array_size; ++i)
+ mask |= 1 << src[i];
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16)
+ bytes = 2;
+
+ uacpi_memcpy(dst, &mask, bytes);
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_2:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_3:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_6:
+ *dst |= *src << insn->f3.imm;
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_AML_SIZE_32:
+ accumulator = aml_size;
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE:
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE_NO_INDEX:
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL: {
+ uacpi_size source_offset, length;
+ uacpi_u8 *dst_string;
+ const uacpi_char *src_string;
+ union {
+ void *ptr;
+ uacpi_resource_source *source;
+ uacpi_resource_label *label;
+ } src_name = { 0 };
+
+ src_name.ptr = src;
+
+ source_offset = base_aml_size_with_header + accumulator;
+ dst_string = dst_base + source_offset;
+
+ if (insn->f1.aml_offset)
+ uacpi_memcpy(dst, &source_offset, sizeof(uacpi_u16));
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE &&
+ src_name.source->index_present)
+ uacpi_memcpy(dst_string++, &src_name.source->index, 1);
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL) {
+ length = src_name.label->length;
+ src_string = src_name.label->string;
+ } else {
+ length = src_name.source->length;
+ src_string = src_name.source->string;
+ }
+
+ if (length == 0)
+ break;
+
+ if (uacpi_unlikely(src_string == UACPI_NULL)) {
+ uacpi_error(
+ "source string length is %zu but the pointer is NULL\n",
+ length
+ );
+ ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ uacpi_memcpy(dst_string, src_string, length);
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_PIN_TABLE_LENGTH:
+ uacpi_memcpy_zerout(&accumulator, src,
+ sizeof(accumulator), sizeof(uacpi_u16));
+ accumulator *= sizeof(uacpi_u16);
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_PIN_TABLE:
+ /*
+ * The pin table resides right at the end of the base resource,
+ * set the offset to it in the AML we're encoding.
+ */
+ uacpi_memcpy(dst, &base_aml_size_with_header, sizeof(uacpi_u16));
+
+ /*
+ * Copy the actual data. It also resides right at the end of the
+ * native base resource.
+ */
+ uacpi_memcpy(
+ dst_base + base_aml_size_with_header,
+ resource_end,
+ accumulator
+ );
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_VENDOR_DATA: {
+ uacpi_u16 vendor_data_length, data_offset, vendor_data_offset;
+ uacpi_u8 *vendor_data;
+
+ // Read the vendor_data pointer
+ uacpi_memcpy(&vendor_data, NATIVE_OFFSET(resource, insn->f3.arg2),
+ sizeof(void*));
+ uacpi_memcpy(&vendor_data_length, src, sizeof(uacpi_u16));
+
+ if (vendor_data == UACPI_NULL) {
+ uacpi_size full_aml_size;
+
+ if (uacpi_unlikely(vendor_data_length != 0)) {
+ uacpi_error(
+ "vendor_data_length is %d, but pointer is NULL\n",
+ vendor_data_length
+ );
+ ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ /*
+ * There's no vendor data. The specification still mandates
+ * that we fill the vendor data offset field correctly, meaning
+ * we set it to the total length of the resource.
+ */
+ full_aml_size = aml_size;
+ full_aml_size += aml_resource_kind_to_header_size[
+ spec->resource_kind
+ ];
+
+ uacpi_memcpy(dst, &full_aml_size, sizeof(uacpi_u16));
+ break;
+ }
+
+ /*
+ * Calculate the offset of vendor data from the end of the native
+ * resource and use it since it matches the offset from the end of
+ * the AML resource.
+ *
+ * Non-zero value means there's a source string in between.
+ */
+ data_offset = vendor_data - (uacpi_u8*)resource_end;
+ vendor_data_offset = data_offset + base_aml_size_with_header;
+
+ // Write vendor_data_offset
+ uacpi_memcpy(dst, &vendor_data_offset, sizeof(uacpi_u16));
+
+ /*
+ * Write vendor_data_length, this field is right after
+ * vendor_data_offset, and is completely redundant, but it exists
+ * nonetheless.
+ */
+ uacpi_memcpy(
+ dst + sizeof(uacpi_u16),
+ &vendor_data_length,
+ sizeof(vendor_data_length)
+ );
+
+ // Finally write the data itself
+ uacpi_memcpy(
+ dst_base + vendor_data_offset,
+ vendor_data,
+ vendor_data_length
+ );
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_SERIAL_TYPE_SPECIFIC: {
+ uacpi_u8 serial_type = *src;
+ *dst = serial_type;
+
+ ctx->st = validate_aml_serial_type(serial_type);
+ if (uacpi_unlikely_error(ctx->st))
+ return UACPI_ITERATION_DECISION_BREAK;
+
+ if (uacpi_unlikely(resource->type !=
+ aml_serial_to_native_type(serial_type))) {
+ uacpi_error(
+ "native serial resource type %d doesn't match expected %d\n",
+ resource->type, aml_serial_to_native_type(serial_type)
+ );
+ ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ // Rebase the end pointer & size now that we know the serial type
+ resource_end = PTR_AT(
+ resource_end,
+ aml_serial_resource_to_extra_native_size[serial_type]
+ );
+ base_aml_size_with_header += aml_serial_resource_to_extra_aml_size[
+ serial_type
+ ];
+
+ accumulator = resource->serial_bus_common.vendor_data_length;
+ if (accumulator == 0)
+ break;
+
+ // Copy vendor data
+ uacpi_memcpy(
+ dst_base + base_aml_size_with_header,
+ resource_end,
+ accumulator
+ );
+ break;
+ }
+
+ CONVERSION_OPCODES_COMMON(src)
+ }
+ }
+}
+
+static uacpi_status native_resources_to_aml(
+ uacpi_resources *native_resources, void *aml_buffer
+)
+{
+ uacpi_status ret;
+ struct resource_conversion_ctx ctx = { 0 };
+
+ ctx.buf = aml_buffer;
+
+ ret = uacpi_for_each_resource(
+ native_resources, do_native_resource_to_aml, &ctx
+ );
+ if (ret == UACPI_STATUS_NO_RESOURCE_END_TAG) {
+ // An end tag is always included
+ uacpi_resource end_tag = { .type = UACPI_RESOURCE_TYPE_END_TAG };
+
+ do_native_resource_to_aml(&ctx, &end_tag);
+ ret = UACPI_STATUS_OK;
+ }
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return ctx.st;
+}
+
+static uacpi_iteration_decision accumulate_aml_buffer_size(
+ void *opaque, uacpi_resource *resource
+)
+{
+ struct resource_conversion_ctx *ctx = opaque;
+ const struct uacpi_resource_spec *spec;
+ uacpi_size size_for_this;
+
+ // resource->type is sanitized to be valid here by the iteration function
+ spec = resource_spec_from_native(resource);
+
+ size_for_this = aml_size_for_native_resource(resource, spec);
+ if (size_for_this == 0 || (ctx->size + size_for_this) < ctx->size) {
+ uacpi_error("invalid aml size for native resource: %zu\n",
+ size_for_this);
+ ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ ctx->size += size_for_this;
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+uacpi_status uacpi_native_resources_to_aml(
+ uacpi_resources *resources, uacpi_object **out_template
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ void *buffer;
+ struct resource_conversion_ctx ctx = { 0 };
+
+ ret = uacpi_for_each_resource(
+ resources, accumulate_aml_buffer_size, &ctx
+ );
+ if (ret == UACPI_STATUS_NO_RESOURCE_END_TAG) {
+ // An end tag is always included
+ uacpi_resource end_tag = { .type = UACPI_RESOURCE_TYPE_END_TAG };
+
+ accumulate_aml_buffer_size(&ctx, &end_tag);
+ ret = UACPI_STATUS_OK;
+ }
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ if (uacpi_unlikely_error(ctx.st))
+ return ctx.st;
+
+ // Same reasoning as native_resource_from_aml
+ if (uacpi_unlikely(ctx.size > (5 * 1024u * 1024u))) {
+ uacpi_error("bug: bogus target aml resource buffer size %zu\n",
+ ctx.size);
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ buffer = uacpi_kernel_alloc_zeroed(ctx.size);
+ if (uacpi_unlikely(buffer == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ obj = uacpi_create_object(UACPI_OBJECT_BUFFER);
+ if (uacpi_unlikely(obj == UACPI_NULL)) {
+ uacpi_free(buffer, ctx.size);
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ obj->buffer->data = buffer;
+ obj->buffer->size = ctx.size;
+
+ ret = native_resources_to_aml(resources, buffer);
+ if (uacpi_unlikely_error(ret))
+ uacpi_object_unref(obj);
+
+ if (ret == UACPI_STATUS_OK)
+ *out_template = obj;
+
+ return ret;
+}
+
+uacpi_status uacpi_set_resources(
+ uacpi_namespace_node *device, uacpi_resources *resources
+)
+{
+ uacpi_status ret;
+ uacpi_object *res_template;
+ uacpi_object_array args;
+
+ ret = uacpi_native_resources_to_aml(resources, &res_template);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ args.objects = &res_template;
+ args.count = 1;
+ ret = uacpi_eval(device, "_SRS", &args, UACPI_NULL);
+
+ uacpi_object_unref(res_template);
+ return ret;
+}
+
+#endif // !UACPI_BAREBONES_MODE