diff options
Diffstat (limited to 'sys/dev/acpi/uacpi/resources.c')
-rw-r--r-- | sys/dev/acpi/uacpi/resources.c | 2569 |
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 |