#include #include #include #include #include #include #include #include #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...] -> pin table * [...] -> source name * [...] -> 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