This mostly fixes the numbering of IRQs when requested. This now works on an old AMD board with broken _CRS. It requests the possible irqs from _PRS and generates a valid _SRS request to set the one from the end of the list.
Note that acpi translator can only be used with gnumach that has been compiled with '--enable-apic --disable-linux-groups' or else throws an assert(irq < NINTR). This is expected. On an intel board here with working _CRS, it prints "Found IRQ resource 11", but then returns an error and rumpdisk cannot start. --- debian/patches/acpi-init-files.diff | 339 +++++++++++++++++++--------- 1 file changed, 233 insertions(+), 106 deletions(-) diff --git a/debian/patches/acpi-init-files.diff b/debian/patches/acpi-init-files.diff index eb7482c..0102e70 100644 --- a/debian/patches/acpi-init-files.diff +++ b/debian/patches/acpi-init-files.diff @@ -1,6 +1,6 @@ --- /dev/null +++ b/acpi_init.c -@@ -0,0 +1,597 @@ +@@ -0,0 +1,724 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +#include <acpi/acpi.h> + @@ -32,6 +32,8 @@ +#define PCI_CFG1_START 0xcf8 +#define PCI_CFG1_END 0xcff + ++#define ISA_IRQS 16 ++ +extern acpi_status acpi_hw_legacy_sleep(u8 sleep_state); + +// Lets keep the ACPI tables in this module @@ -361,162 +363,182 @@ + acpi_hw_legacy_sleep (sleep_state); +} + ++ +static acpi_status -+register_slot(acpi_handle handle, u32 lev, void *context, void **rv) ++acpi_pci_link_count_all(struct acpi_resource *resource, void *context) +{ -+ if (!context) -+ return AE_ERROR; ++ int *count = (int *)context; + -+ u64 addr, addr_bus; -+ acpi_status err; ++ (*count)++; + -+ struct slots *s = (void *)malloc(sizeof (struct slots)); -+ if (!s) -+ return -1; -+ -+ { -+ struct acpi_buffer buffer = { 0, NULL }; -+ union acpi_object element; -+ buffer.length = sizeof(union acpi_object); -+ buffer.pointer = &element; -+ err = acpi_evaluate_object(handle, METHOD_NAME__BBN, NULL, &buffer); -+ if (ACPI_SUCCESS(err) && element.type == ACPI_TYPE_INTEGER) -+ addr_bus = element.integer.value; -+ else if (err == AE_NOT_FOUND) -+ addr_bus = 0; -+ else -+ { -+ free(s); -+ return AE_ERROR; -+ } -+ } -+ -+ s->bus = addr_bus & 0xff; -+ -+ { -+ struct acpi_buffer buffer = { 0, NULL }; -+ union acpi_object element; -+ buffer.length = sizeof(union acpi_object); -+ buffer.pointer = &element; -+ -+ err = acpi_evaluate_object(handle, METHOD_NAME__ADR, NULL, &buffer); -+ if (ACPI_SUCCESS(err) && element.type == ACPI_TYPE_INTEGER) -+ addr = element.integer.value; -+ else if (err == AE_NOT_FOUND) -+ { -+ free(s); -+ return AE_OK; -+ } -+ else -+ { -+ free(s); -+ return AE_ERROR; -+ } -+ } -+ -+ s->dev = (addr >> 16) & 0xffff; -+ s->func = addr & 0xffff; -+ s->handle = handle; -+ s->next = NULL; -+ -+ struct slots **slot_ptr_ptr = (struct slots **)(context); -+ if (!(*slot_ptr_ptr)) -+ *slot_ptr_ptr = s; -+ else ++ return AE_OK; ++} ++ ++static acpi_status ++acpi_pci_link_count_irq(struct acpi_resource *resource, void *context) ++{ ++ int *count = (int *)context; ++ ++ if (!resource->length) ++ return AE_OK; ++ ++ switch (resource->type) + { -+ struct slots *slot_ptr = *slot_ptr_ptr; -+ while(slot_ptr->next) -+ slot_ptr = slot_ptr->next; -+ slot_ptr->next = s; ++ case ACPI_RESOURCE_TYPE_IRQ: ++ { ++ struct acpi_resource_irq *irq_resource = &resource->data.irq; ++ if (!irq_resource || !irq_resource->interrupt_count || !irq_resource->interrupts[0]) ++ return AE_OK; ++ (*count)++; ++ } ++ break; ++ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: ++ { ++ struct acpi_resource_extended_irq *irq_resource = &resource->data.extended_irq; ++ if (!irq_resource || !irq_resource->interrupt_count || !irq_resource->interrupts[0]) ++ return AE_OK; ++ (*count)++; ++ } ++ break; ++ default: ++ break; + } ++ + return AE_OK; +} + +static acpi_status -+acpi_pci_link_check(struct acpi_resource *resource, void *context) ++acpi_pci_link_subset_last_irq(struct acpi_resource *resource, void *context) ++{ ++ struct acpi_resource *res = (struct acpi_resource *)context; ++ ++ switch (resource->type) ++ { ++ case ACPI_RESOURCE_TYPE_IRQ: ++ { ++ struct acpi_resource_irq *irq_resource = &resource->data.irq; ++ if (!irq_resource || !irq_resource->interrupt_count || !irq_resource->interrupts[0]) ++ return AE_OK; ++ *res = *resource; ++ return AE_OK; ++ } ++ break; ++ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: ++ { ++ struct acpi_resource_extended_irq *irq_resource = &resource->data.extended_irq; ++ if (!irq_resource || !irq_resource->interrupt_count || !irq_resource->interrupts[0]) ++ return AE_OK; ++ *res = *resource; ++ return AE_OK; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return AE_OK; ++} ++ ++static acpi_status ++acpi_pci_link_get_last_valid_irq(struct acpi_resource *resource, void *context) +{ + int *irq = (int *)context; ++ int index = *irq; ++ ++ if (index) ++ { ++ while (index) ++ { ++ if ((resource->type == ACPI_RESOURCE_TYPE_START_DEPENDENT) ++ || (resource->type == ACPI_RESOURCE_TYPE_END_DEPENDENT)) ++ { ++ resource = ACPI_NEXT_RESOURCE(resource); ++ continue; ++ } ++ resource = ACPI_NEXT_RESOURCE(resource); ++ index--; ++ } ++ } ++ ++ if (!resource->length) ++ { ++ acpi_os_printf("Empty resource\n"); ++ return AE_OK; ++ } + + switch (resource->type) + { + case ACPI_RESOURCE_TYPE_IRQ: + { + struct acpi_resource_irq *irq_resource = &resource->data.irq; -+ if (!irq_resource || !irq_resource->interrupt_count) ++ if (!irq_resource || !irq_resource->interrupt_count || !irq_resource->interrupts[0]) + { + // no IRQ# bits set generally w/ _STA disabled + acpi_os_printf("Empty _CRS IRQ resource\n"); + return AE_OK; + } + *irq = irq_resource->interrupts[0]; -+ break; + } ++ break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + { + struct acpi_resource_extended_irq *irq_resource = &resource->data.extended_irq; -+ if (!irq_resource || !irq_resource->interrupt_count) ++ if (!irq_resource || !irq_resource->interrupt_count || !irq_resource->interrupts[0]) + { + // extended IRQ descriptors should return at least 1 IRQ + acpi_os_printf("Empty _CRS EXT IRQ resource\n"); + return AE_OK; + } + *irq = irq_resource->interrupts[0]; -+ break; + } ++ break; ++ case ACPI_RESOURCE_TYPE_END_TAG: ++ acpi_os_printf("Chose final irq %d\n", *irq); ++ return AE_OK; ++ break; + default: ++ acpi_os_printf("Not IRQ resource\n"); + return AE_OK; + } + -+ return AE_CTRL_TERMINATE; ++ acpi_os_printf("Found IRQ resource %d\n", *irq); ++ return AE_OK; +} + +int +acpi_get_irq_number(uint16_t bus, uint16_t dev, uint16_t func) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; ++ struct acpi_buffer srs_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_pci_routing_table *entry; ++ struct acpi_resource *res; ++ uint8_t *buf; ++ int srs_count; ++ + acpi_handle handle = ACPI_ROOT_OBJECT; + acpi_handle parent_handle = NULL; ++ acpi_handle prt = NULL; + acpi_handle lnk = NULL; + acpi_status err = AE_OK; + u16 prt_dev, prt_func; -+ struct slots *pcislots = NULL, *iter; -+ int nslots = 0; + -+ err = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, INT_MAX, register_slot, NULL, (struct slots **)&pcislots, NULL); ++ err = acpi_get_handle(handle, "\\_SB.PCI0", &parent_handle); + if (ACPI_FAILURE(err)) + return -EIO; + -+ for (iter = pcislots; iter; iter = iter->next) -+ { -+ if ((iter->bus == bus) && (iter->dev == dev) && (iter->func == func)) -+ { -+ handle = iter->handle; -+ break; -+ } -+ nslots++; -+ } ++ err = acpi_get_handle(parent_handle, METHOD_NAME__PRT, &prt); ++ if (ACPI_FAILURE(err)) ++ return -EIO; + -+ err = acpi_get_parent(handle, &parent_handle); ++ err = acpi_evaluate_object(prt, NULL, NULL, &buffer); + if (ACPI_FAILURE(err)) + return -EIO; + + err = acpi_get_irq_routing_table(parent_handle, &buffer); + if (ACPI_FAILURE(err)) -+ { -+ free(buffer.pointer); -+ // free pcislots allocated in register_slot function -+ struct slots *current_pcislot = pcislots; -+ while (current_pcislot) -+ { -+ free(current_pcislot); -+ pcislots = pcislots->next; -+ current_pcislot = pcislots; -+ } -+ return -EIO; -+ } ++ return -EIO; + -+ entry = ACPI_CAST_PTR(struct acpi_pci_routing_table, buffer.pointer); ++ entry = ACPI_CAST_PTR(struct acpi_pci_routing_table, ACPI_CAST_PTR(u8, buffer.pointer)); + while (entry && (entry->length > 0)) + { + /* Already applies to the bus of the device */ @@ -525,28 +547,133 @@ + if ((prt_dev == dev) && (prt_func == 0xffff)) + { + if (entry->source[0]) -+ { ++ { ++ int crs_count = 0; ++ int prs_count_irq = 0; ++ int prs_count_all = 0; ++ int irq = -1; ++ + /* Dynamic: + * - source field specifies PCI interrupt LNK + * - source_index specifies which resource descriptor in + * resource template of LNK to allocate this interrupt + */ -+ err = acpi_get_handle(handle, entry->source, &lnk); ++ err = acpi_get_handle(ACPI_ROOT_OBJECT, entry->source, &lnk); + if (ACPI_FAILURE(err)) + return -EIO; + + if (!lnk) -+ acpi_os_printf("error invalid lnk"); -+ -+ int irq = -1; -+ err = acpi_walk_resources(lnk, METHOD_NAME__CRS, acpi_pci_link_check, &irq); ++ { ++ acpi_os_printf("Error invalid lnk\n"); ++ return -EIO; ++ } ++ ++ err = acpi_walk_resources(lnk, "_CRS", acpi_pci_link_count_irq, &crs_count); ++ if (ACPI_FAILURE(err) || !crs_count) ++ acpi_os_printf("Warning: missing _CRS\n"); ++ acpi_walk_resources(lnk, "_PRS", acpi_pci_link_count_all, &prs_count_all); ++ err = acpi_walk_resources(lnk, "_PRS", acpi_pci_link_count_irq, &prs_count_irq); + if (ACPI_FAILURE(err)) -+ return -EIO; -+ return irq; -+ // FIXME check the LNK irq _CRS and then set it with _SRS ++ { ++ if (crs_count == 0) ++ { ++ acpi_os_printf("Error: missing _PRS when needed\n"); ++ return -EIO; ++ } ++ } ++ if (crs_count > 0) ++ { ++ irq = 0; ++ err = acpi_walk_resources(lnk, "_CRS", acpi_pci_link_get_last_valid_irq, &irq); ++ if (ACPI_FAILURE(err) || !irq) ++ { ++ acpi_os_printf("Error walk_resources _CRS\n"); ++ return -EIO; ++ } ++ ++ err = acpi_get_current_resources(lnk, &srs_buffer); ++ if (ACPI_FAILURE(err)) ++ { ++ acpi_os_printf("Error getting _CRS\n"); ++ return -EIO; ++ } ++ ++ err = acpi_set_current_resources(lnk, &srs_buffer); ++ if (ACPI_FAILURE(err)) ++ { ++ acpi_os_printf("Error setting _SRS\n"); ++ return -EIO; ++ } ++ return irq + ISA_IRQS; ++ } ++ else if (prs_count_irq > 0) ++ { ++ irq = 0; ++ err = acpi_walk_resources(lnk, "_PRS", acpi_pci_link_get_last_valid_irq, &irq); ++ if (ACPI_FAILURE(err) || !irq) ++ { ++ acpi_os_printf("Error walk_resources _PRS\n"); ++ return -EIO; ++ } ++ ++ if (irq > 16) ++ { ++ acpi_os_printf("Error IRQ > 16, not implemented\n"); ++ return -EIO; ++ } ++ ++ /* Found a possible irq, but need to call _SRS to set it */ ++ ++ /* IRQ resource + end tagged resource */ ++ srs_count = 2; ++ ++ res = (struct acpi_resource *)calloc(1, srs_count * sizeof(struct acpi_resource)); ++ ++ err = acpi_walk_resources(lnk, "_PRS", acpi_pci_link_subset_last_irq, res); ++ if (err) ++ { ++ acpi_os_printf("Error _PRS cannot be read\n"); ++ free(res); ++ return -EIO; ++ } ++ ++ /* Patch the _PRS to use for _SRS */ ++ srs_buffer.length = sizeof(struct acpi_resource) + 1; ++ srs_buffer.pointer = res; ++ ++ switch (res->type) ++ { ++ case ACPI_RESOURCE_TYPE_IRQ: ++ res->data.irq.interrupt_count = 1; ++ res->data.irq.interrupts[0] = irq; ++ break; ++ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: ++ res->data.extended_irq.interrupt_count = 1; ++ res->data.extended_irq.interrupts[0] = irq; ++ break; ++ } ++ ++ res[1].type = ACPI_RESOURCE_TYPE_END_TAG; ++ res[1].length = sizeof(struct acpi_resource); ++ ++ err = acpi_set_current_resources(lnk, &srs_buffer); ++ if (ACPI_FAILURE(err)) ++ { ++ acpi_os_printf("Error setting _SRS\n"); ++ free(res); ++ return -EIO; ++ } ++ free(res); ++ return irq + ISA_IRQS; ++ } ++ else ++ { ++ acpi_os_printf("Error: _CRS has empty IRQ resources\n"); ++ return -EIO; ++ } + } -+ else -+ { ++ else ++ { + /* Static: + * - source field is zero + * - source_index specifies IRQ value hardwired to -- 2.45.2