Have acpipci(4) look for a matching SMMU in the IORT.
authorpatrick <patrick@openbsd.org>
Sun, 28 Feb 2021 21:42:08 +0000 (21:42 +0000)
committerpatrick <patrick@openbsd.org>
Sun, 28 Feb 2021 21:42:08 +0000 (21:42 +0000)
ok kettenis@

sys/arch/arm64/dev/acpipci.c

index c694464..4b63fa5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: acpipci.c,v 1.25 2021/02/25 23:07:48 patrick Exp $    */
+/*     $OpenBSD: acpipci.c,v 1.26 2021/02/28 21:42:08 patrick Exp $    */
 /*
  * Copyright (c) 2018 Mark Kettenis
  *
@@ -34,6 +34,8 @@
 #include <dev/pci/pcivar.h>
 #include <dev/pci/ppbreg.h>
 
+#include <arm64/dev/acpiiort.h>
+
 struct acpipci_mcfg {
        SLIST_ENTRY(acpipci_mcfg) am_list;
 
@@ -346,6 +348,72 @@ acpipci_conf_write(void *v, pcitag_t tag, int reg, pcireg_t data)
 int
 acpipci_probe_device_hook(void *v, struct pci_attach_args *pa)
 {
+       struct acpipci_mcfg *am = v;
+       struct acpi_table_header *hdr;
+       struct acpi_iort *iort = NULL;
+       struct acpi_iort_node *node;
+       struct acpi_iort_mapping *map;
+       struct acpi_iort_rc_node *rc;
+       struct acpi_q *entry;
+       uint32_t rid, offset;
+       int i;
+
+       rid = pci_requester_id(pa->pa_pc, pa->pa_tag);
+
+       /* Look for IORT table. */
+       SIMPLEQ_FOREACH(entry, &acpi_softc->sc_tables, q_next) {
+               hdr = entry->q_table;
+               if (strncmp(hdr->signature, IORT_SIG,
+                   sizeof(hdr->signature)) == 0) {
+                       iort = entry->q_table;
+                       break;
+               }
+       }
+       if (iort == NULL)
+               return 0;
+
+       /* Find our root complex. */
+       offset = iort->offset;
+       for (i = 0; i < iort->number_of_nodes; i++) {
+               node = (struct acpi_iort_node *)((char *)iort + offset);
+               if (node->type == ACPI_IORT_ROOT_COMPLEX) {
+                       rc = (struct acpi_iort_rc_node *)&node[1];
+                       if (rc->segment == am->am_segment)
+                               break;
+               }
+               offset += node->length;
+       }
+
+       /* No RC found? Weird. */
+       if (i >= iort->number_of_nodes)
+               return 0;
+
+       /* Find our output base towards SMMU. */
+       map = (struct acpi_iort_mapping *)((char *)node + node->mapping_offset);
+       for (i = 0; i < node->number_of_mappings; i++) {
+               offset = map[i].output_reference;
+
+               if (map[i].flags & ACPI_IORT_MAPPING_SINGLE) {
+                       rid = map[i].output_base;
+                       break;
+               }
+
+               /* Mapping encodes number of IDs in the range minus one. */
+               if (map[i].input_base <= rid &&
+                   rid <= map[i].input_base + map[i].number_of_ids) {
+                       rid = map[i].output_base + (rid - map[i].input_base);
+                       break;
+               }
+       }
+
+       /* No mapping found? Even weirder. */
+       if (i >= node->number_of_mappings)
+               return 0;
+
+       node = (struct acpi_iort_node *)((char *)iort + offset);
+       if (node->type == ACPI_IORT_SMMU)
+               acpiiort_smmu_hook(node, rid, pa);
+
        return 0;
 }