Use IORT table to map requester ID into MSI sideband data.
authorkettenis <kettenis@openbsd.org>
Sat, 11 Aug 2018 20:46:48 +0000 (20:46 +0000)
committerkettenis <kettenis@openbsd.org>
Sat, 11 Aug 2018 20:46:48 +0000 (20:46 +0000)
sys/arch/arm64/dev/acpipci.c

index b6f992d..954aaf8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: acpipci.c,v 1.4 2018/08/03 22:40:05 kettenis Exp $    */
+/*     $OpenBSD: acpipci.c,v 1.5 2018/08/11 20:46:48 kettenis Exp $    */
 /*
  * Copyright (c) 2018 Mark Kettenis
  *
@@ -68,6 +68,7 @@ struct acpipci_softc {
        char            sc_ioex_name[32];
        char            sc_memex_name[32];
        int             sc_bus;
+       uint32_t        sc_seg;
 };
 
 int    acpipci_match(struct device *, void *, void *);
@@ -108,6 +109,8 @@ void        *acpipci_intr_establish(void *, pci_intr_handle_t, int,
            int (*)(void *), void *, char *);
 void   acpipci_intr_disestablish(void *, void *);
 
+uint32_t acpipci_iort_map_msi(pci_chipset_tag_t, pcitag_t);
+
 int
 acpipci_match(struct device *parent, void *match, void *aux)
 {
@@ -125,6 +128,7 @@ acpipci_attach(struct device *parent, struct device *self, void *aux)
        struct pcibus_attach_args pba;
        struct aml_value res;
        uint64_t bbn = 0;
+       uint64_t seg = 0;
 
        /* Bail out early if we don't have a valid MCFG table. */
        if (pci_mcfg_addr == 0 || pci_mcfg_max_bus <= pci_mcfg_min_bus) {
@@ -144,11 +148,18 @@ acpipci_attach(struct device *parent, struct device *self, void *aux)
        aml_evalinteger(sc->sc_acpi, sc->sc_node, "_BBN", 0, NULL, &bbn);
        sc->sc_bus = bbn;
 
+       aml_evalinteger(sc->sc_acpi, sc->sc_node, "_SEG", 0, NULL, &seg);
+       sc->sc_seg = seg;
+
        sc->sc_iot = pci_mcfgt;
        sc->sc_ioh = pci_mcfgh;
 
        printf("\n");
 
+       /* XXX We only support segment 0 for now. */
+       if (seg != 0)
+               return;
+
        /* Create extents for our address spaces. */
        snprintf(sc->sc_busex_name, sizeof(sc->sc_busex_name),
            "%s pcibus", sc->sc_dev.dv_xname);
@@ -453,8 +464,8 @@ acpipci_intr_establish(void *v, pci_intr_handle_t ihp, int level,
                pcireg_t reg;
                int off;
 
-               /* Assume hardware passes Requester ID as sideband data. */
-               data = pci_requester_id(ih->ih_pc, ih->ih_tag);
+               /* Map Requester ID through IORT to get sideband data. */
+               data = acpipci_iort_map_msi(ih->ih_pc, ih->ih_tag);
                cookie = ic->ic_establish_msi(ic->ic_cookie, &addr,
                    &data, level, func, arg, name);
                if (cookie == NULL)
@@ -553,3 +564,118 @@ pci_mcfg_init(bus_space_tag_t iot, bus_addr_t addr, int min_bus, int max_bus)
 
        return pc;
 }
+
+/*
+ * IORT support.
+ */
+
+struct acpi_iort {
+       struct acpi_table_header        hdr;
+#define IORT_SIG       "IORT"
+       uint32_t        number_of_nodes;
+       uint32_t        offset;
+       uint32_t        reserved;
+} __packed;
+
+struct acpi_iort_node {
+       uint8_t         type;
+#define ACPI_IORT_ITS          0
+#define ACPI_IORT_ROOT_COMPLEX 2
+       uint16_t        length;
+       uint8_t         revision;
+       uint32_t        reserved1;
+       uint32_t        number_of_mappings;
+       uint32_t        mapping_offset;
+       uint64_t        memory_access_properties;
+       uint32_t        atf_attributes;
+       uint32_t        segment;
+       uint8_t         memory_address_size_limit;
+       uint8_t         reserved2[3];
+} __packed;
+
+struct acpi_iort_mapping {
+       uint32_t        input_base;
+       uint32_t        length;
+       uint32_t        output_base;
+       uint32_t        output_reference;
+       uint32_t        flags;
+#define ACPI_IORT_MAPPING_SINGLE       0x00000001
+} __packed;
+
+uint32_t
+acpipci_iort_map_node(struct acpi_iort_node *node, uint32_t id, uint32_t reference)
+{
+       struct acpi_iort_mapping *map =
+           (struct acpi_iort_mapping *)((char *)node + node->mapping_offset);
+       int i;
+       
+       for (i = 0; i < node->number_of_mappings; i++) {
+               if (map[i].output_reference != reference)
+                       continue;
+               
+               if (map[i].flags & ACPI_IORT_MAPPING_SINGLE)
+                       return map[i].output_base;
+
+               if (map[i].input_base <= id &&
+                   id < map[i].input_base + map[i].length)
+                       return map[i].output_base + (id - map[i].input_base);
+       }
+
+       return id;
+}
+
+uint32_t
+acpipci_iort_map_msi(pci_chipset_tag_t pc, pcitag_t tag)
+{
+       struct acpipci_softc *sc = pc->pc_intr_v;
+       struct acpi_table_header *hdr;
+       struct acpi_iort *iort = NULL;
+       struct acpi_iort_node *node;
+       struct acpi_q *entry;
+       uint32_t rid, its = 0;
+       uint32_t offset;
+       int i;
+
+       rid = pci_requester_id(pc, tag);
+
+       /* Look for IORT table. */
+       SIMPLEQ_FOREACH(entry, &sc->sc_acpi->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 rid;
+
+       /* Find reference to ITS group. */
+       offset = iort->offset;
+       for (i = 0; i < iort->number_of_nodes; i++) {
+               node = (struct acpi_iort_node *)((char *)iort + offset);
+               switch (node->type) {
+               case ACPI_IORT_ITS:
+                       its = offset;
+                       break;
+               }
+               offset += node->length;
+       }
+       if (its == 0)
+               return rid;
+
+       /* Find our root complex and map. */
+       offset = iort->offset;
+       for (i = 0; i < iort->number_of_nodes; i++) {
+               node = (struct acpi_iort_node *)((char *)iort + offset);
+               switch (node->type) {
+               case ACPI_IORT_ROOT_COMPLEX:
+                       if (node->segment == sc->sc_seg)
+                               return acpipci_iort_map_node(node, rid, its);
+                       break;
+               }
+               offset += node->length;
+       }
+
+       return rid;
+}