[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 1/2] ARM: ACPI: IORT: Estimate the size of hardware domain IORT table



From: Manish Jaggi <mjaggi@xxxxxxxxxx>

This patch estimates size of hardware domain IORT table by parsing all
the pcirc nodes and their idmaps, and thereby calculating size by
removing smmu nodes.

Hardware domain IORT table will have only ITS and PCIRC nodes, and PCIRC
nodes' idmap will have output refrences to ITS group nodes.

Signed-off-by: Manish Jaggi <mjaggi@xxxxxxxxxx>
---
 xen/arch/arm/acpi/Makefile  |   1 +
 xen/arch/arm/acpi/iort.c    | 242 ++++++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/domain_build.c |  11 +-
 xen/include/asm-arm/iort.h  |  14 +++
 4 files changed, 267 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/acpi/Makefile b/xen/arch/arm/acpi/Makefile
index 23963f8..93d8868 100644
--- a/xen/arch/arm/acpi/Makefile
+++ b/xen/arch/arm/acpi/Makefile
@@ -1,2 +1,3 @@
 obj-y += lib.o
 obj-y += boot.init.o
+obj-y += iort.o
diff --git a/xen/arch/arm/acpi/iort.c b/xen/arch/arm/acpi/iort.c
new file mode 100644
index 0000000..01914cb
--- /dev/null
+++ b/xen/arch/arm/acpi/iort.c
@@ -0,0 +1,242 @@
+#include <xen/init.h>
+#include <xen/compile.h>
+#include <xen/lib.h>
+#include <xen/mm.h>
+#include <xen/domain_page.h>
+#include <xen/sched.h>
+#include <xen/acpi.h>
+#include <acpi/actables.h>
+#include <xen/list.h>
+
+struct pcirc_idmap
+{
+    struct acpi_iort_id_mapping idmap;
+    struct list_head entry;
+};
+
+
+int add_to_new_idmap_list(struct list_head *new_idmap_list,
+        unsigned int ib, unsigned int ob,
+        unsigned int oref, unsigned int idc)
+{
+    struct pcirc_idmap *new_e = xzalloc(struct pcirc_idmap);
+    if ( !new_e )
+    {
+        printk("Unable to allocate memory\n");
+        return -ENOMEM;
+    }
+
+    new_e->idmap.input_base = ib;
+    new_e->idmap.output_base = ob;
+    new_e->idmap.output_reference = oref;
+    new_e->idmap.id_count = idc;
+
+    list_add_tail(&new_e->entry, new_idmap_list);
+
+    return 0;
+}
+
+/*
+ * returns the number of pci_idmaps created as a result of parsing
+ * the smmu nodes for this pci_idmap
+ * the pci_idmaps are added to the new_idmap_list
+ *
+ * if get_count_only is set new_idmap_list is not populated, just the
+ * id_count is updated.
+ *
+ * This method is called in estimate size flow and write hwdom iort flow
+ */
+static int update_id_mapping(struct acpi_iort_id_mapping *pci_idmap,
+        struct acpi_iort_node *smmu_node,
+        struct list_head *new_idmap_list /* [out] */,
+        int get_count_only,
+        unsigned int *id_count /* [out] */)
+{
+    /* local varialbes to hold input output base and count values.
+     * p_ for pci idmap; s for smmu idmap */
+    unsigned int p_ib, p_ob, p_idc, s_ib, s_ob, s_idc, delta, i;
+    struct acpi_iort_id_mapping *smmu_idmap = NULL;
+    int ret;
+
+    p_ib = pci_idmap->input_base;
+    p_ob = pci_idmap->output_base;
+    p_idc = pci_idmap->id_count;
+
+    if(get_count_only)
+        *id_count = 0;
+
+    for ( i = 0; i < smmu_node->mapping_count; i++ )
+    {
+        smmu_idmap = (struct acpi_iort_id_mapping*)((u8*)smmu_node
+                + smmu_node->mapping_offset);
+        s_ib = smmu_idmap->input_base;
+        s_ob = smmu_idmap->output_base;
+        s_idc = smmu_idmap->id_count;
+
+        if ( s_ib <= p_ob )
+        {
+            if ( s_ib + s_idc < p_ob )
+                continue;
+
+            delta = p_ob - s_ib;
+
+            if ( get_count_only )
+            {
+                (*id_count)++;
+                continue;
+            }
+
+            ret = add_to_new_idmap_list(new_idmap_list, p_ib, s_ob + delta,
+                    smmu_idmap->output_reference,
+                    s_ib + s_idc <= p_ob + p_idc ? s_idc - delta : p_idc);
+            if (ret)
+                goto err;
+        }
+        else
+        {
+            if( p_ob + p_idc < s_ib )
+                continue;
+
+            delta = s_ib - p_ob;
+
+            if ( get_count_only )
+            {
+                (*id_count)++;
+                continue;
+            }
+
+            ret = add_to_new_idmap_list(new_idmap_list, p_ib + delta, s_ob,
+                    smmu_idmap->output_reference,
+                    s_ib + s_idc < p_ob + p_idc ? s_idc : p_idc - delta);
+            if (ret)
+                goto err;
+        }
+    }
+
+    return 0;
+err:
+    return ret;
+}
+
+/*
+ *  This method scans the idarray for a pcirc_node
+ *  For each idmap there could be one or more entries in smmu idmap array
+ *
+ *  update_id_mapping will add correspoding smmu idmap entries but will
+ *  change mapping, so a pci idmap entry would have output_ref as its group
+ *
+ * idlist is populated when get_num_ids == 0
+ * - stores updated ids by removing smmu output reference
+ */
+static int scan_idarray(u8 *iort_base_ptr, struct acpi_iort_node *node,
+        struct list_head *idmaplist, int get_num_ids,
+        unsigned int *num_ids)
+{
+    int i = 0, ret = 0;
+    unsigned int id_count = 0;
+    struct acpi_iort_node *onode; /* output node */
+    struct acpi_iort_id_mapping *idmap = (struct acpi_iort_id_mapping*)
+                                        ((u8*)node + node->mapping_offset);
+
+    for ( i = 0; i < node->mapping_count; i++ )
+    {
+        onode = (struct acpi_iort_node*)(iort_base_ptr +
+                idmap->output_reference);
+        switch ( onode->type )
+        {
+            case ACPI_IORT_NODE_ITS_GROUP:
+                continue;
+            case ACPI_IORT_NODE_SMMU:
+            case ACPI_IORT_NODE_SMMU_V3:
+                ret = update_id_mapping(idmap, onode, idmaplist, get_num_ids,
+                        &id_count);
+                if ( ret )
+                    goto end;
+                if (get_num_ids)
+                    *num_ids += id_count;
+            break;
+        }
+        idmap++;
+    }
+
+end:
+    return ret;
+}
+
+/*
+ * This method estimates the size of pci_rc node
+ * and returns any extra bytes added due to accomodting smmu_id_array elements
+ * which map to a single element of pci_rc id_array.
+ */
+static int estimate_pcirc_node_size(u8 *iort_base_ptr,
+        struct acpi_iort_node *node,
+        unsigned int *node_size)
+{
+    int ret;
+    unsigned int num_ids = 0;
+
+    *node_size = 0;
+    ret = scan_idarray(iort_base_ptr, node, NULL, 1, &num_ids);
+    if ( !ret )
+        *node_size += num_ids*sizeof(struct acpi_iort_id_mapping) +
+            sizeof(struct acpi_iort_node) -1 +
+            sizeof(struct acpi_iort_root_complex);
+
+    return ret;
+}
+
+/*
+ *  This method parses the iort_table pci_rc nodes.
+ *  For each element in the pci_rc: id_array smmu output reference
+ *    has to be patched up with ITS group output reference.
+ *  Each element in the id_array may map to mulpile entries in smmu id_array.
+ *    Thus when patching the smmu output refrence, extra elements in id_array
+ *    need to be created.
+ *  Thus the size of the resultant id_array need to identified before the 
actual
+ *    patching is done hardware domains's IORT table will have PCIRC
+ *    and ITS group nodes only.
+ */
+int estimate_iort_size(size_t *iort_size)
+{
+    unsigned int i;
+    unsigned int node_size = 0;
+    struct acpi_table_iort *fw_iort;
+    struct acpi_iort_node *node = NULL;
+
+    if ( acpi_get_table(ACPI_SIG_IORT, 0,
+                (struct acpi_table_header **)&fw_iort) )
+    {
+        printk("Failed to get IORT table\n");
+        goto err;
+    }
+
+    *iort_size = sizeof(struct acpi_table_iort);
+    node = (struct acpi_iort_node *)((u8*)fw_iort + fw_iort->node_offset);
+
+    if (!node)
+        goto err;
+
+    /* Iterate over the pci_rc nodes in firmware iort table */
+    for ( i = 0; i < fw_iort->node_count; i++ )
+    {
+        switch (node->type)
+        {
+            case ACPI_IORT_NODE_PCI_ROOT_COMPLEX:
+                if( !estimate_pcirc_node_size((u8*)fw_iort, node, &node_size) )
+                    *iort_size += node_size;
+                else
+                    goto err;
+                break;
+
+            case ACPI_IORT_NODE_ITS_GROUP:
+                *iort_size += node->length;
+                break;
+        }
+
+        node = (struct acpi_iort_node *)((uint8_t*)node + node->length);
+    }
+
+    return 0;
+err:
+    return -EINVAL;
+}
diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index 5739ea4..0328926 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -20,6 +20,7 @@
 #include <asm/psci.h>
 #include <asm/setup.h>
 #include <asm/cpufeature.h>
+#include <asm/iort.h>
 
 #include <asm/gic.h>
 #include <xen/irq.h>
@@ -1796,7 +1797,7 @@ static int acpi_create_fadt(struct domain *d, struct 
membank tbl_add[])
 
 static int estimate_acpi_efi_size(struct domain *d, struct kernel_info *kinfo)
 {
-    size_t efi_size, acpi_size, madt_size;
+    size_t efi_size, acpi_size, madt_size, iort_size;
     u64 addr;
     struct acpi_table_rsdp *rsdp_tbl;
     struct acpi_table_header *table;
@@ -1837,6 +1838,14 @@ static int estimate_acpi_efi_size(struct domain *d, 
struct kernel_info *kinfo)
     acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
 
     acpi_size += ROUNDUP(sizeof(struct acpi_table_rsdp), 8);
+
+    if( estimate_iort_size(&iort_size) )
+    {
+        printk("Unable to get hwdom iort size\n");
+        return -EINVAL;
+    }
+    acpi_size += iort_size;
+
     d->arch.efi_acpi_len = PAGE_ALIGN(ROUNDUP(efi_size, 8)
                                       + ROUNDUP(acpi_size, 8));
 
diff --git a/xen/include/asm-arm/iort.h b/xen/include/asm-arm/iort.h
new file mode 100644
index 0000000..3b4cd7b
--- /dev/null
+++ b/xen/include/asm-arm/iort.h
@@ -0,0 +1,14 @@
+#ifndef _XEN_ASM_IORT_H
+#define _XEN_ASM_IORT_H
+
+int estimate_iort_size(size_t *iort_size);
+
+#endif /* _XEN_ASM_IORT_H */
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
-- 
2.7.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.