]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
plat/drivers/gic: Enable `GICv2` probing through `ACPI`
authorSergiu Moga <sergiu.moga@protonmail.com>
Tue, 23 May 2023 13:00:27 +0000 (16:00 +0300)
committerUnikraft <monkey@unikraft.io>
Fri, 11 Aug 2023 16:42:01 +0000 (16:42 +0000)
If `ACPI` is enabled, rely on it for probing the `GICv2`. Thus, we
need to take two steps for this:
- fetch the GIC CPU interface's base address and default length from
the `GICC` `MADT` entry. Since there is only one `Redistributor`, all
`GICC`'s must have the same physical base address.
- fetch the `GIC Distributor`'s physical base
address from `MADT`'s `GICD` structure and check that the size is
compliant with that of `GICv2`.

`GICR Base Address` must be set to 0, otherwise this is `GICv3/4`.

Furthermore, separate the two ways of probing `GICv2` in two:
one for `ACPI` and one for `Devicetree`.

Signed-off-by: Sergiu Moga <sergiu.moga@protonmail.com>
Reviewed-by: Razvan Virtan <virtanrazvan@gmail.com>
Reviewed-by: Michalis Pappas <michalis@unikraft.io>
Approved-by: Razvan Deaconescu <razvand@unikraft.io>
Tested-by: Unikraft CI <monkey@unikraft.io>
GitHub-Closes: #912

plat/drivers/gic/gic-common.c
plat/drivers/gic/gic-v2.c
plat/drivers/include/gic/gic-v2.h

index d66dfd694d0100cf9dbb92420955870ce52acdd7..c2949dd37551b81b123014d3355442d576af1602 100644 (file)
@@ -47,7 +47,7 @@ int _dtb_init_gic(const void *fdt, struct _gic_dev **dev)
 
 #ifdef CONFIG_LIBGICV2
        /* First, try GICv2 */
-       rc = gicv2_probe(fdt, dev);
+       rc = gicv2_probe(dev);
        if (rc == 0)
                return 0;
 #endif /* CONFIG_LIBGICV2 */
index 0fe4f7b9fb683c18b1f870c8361c54b149734a3a..77b175dcdfc7b9cf9d960b9dafd20a84c5d60ada 100644 (file)
 #include <uk/asm.h>
 #include <uk/arch/limits.h>
 #include <uk/plat/lcpu.h>
+#ifdef CONFIG_UKPLAT_ACPI
+#include <uk/plat/common/acpi.h>
+#endif /* CONFIG_UKPLAT_ACPI */
+#include <uk/plat/common/bootinfo.h>
 #include <uk/plat/common/irq.h>
 #include <uk/plat/spinlock.h>
 #include <arm/cpu.h>
@@ -69,7 +73,7 @@ struct _gic_dev gicv2_drv = {
 #endif /* CONFIG_HAVE_SMP */
 };
 
-static const char * const gic_device_list[] = {
+static const char * const gic_device_list[] __maybe_unused = {
        "arm,cortex-a15-gic",
        NULL
 };
@@ -490,9 +494,8 @@ static int gicv2_initialize(void)
        return 0;
 }
 
-static int gicv2_do_probe(const void *fdt)
+static void gicv2_set_ops(void)
 {
-       int fdt_gic, r;
        struct _gic_operations drv_ops = {
                .initialize        = gicv2_initialize,
                .ack_irq           = gicv2_ack_irq,
@@ -509,6 +512,68 @@ static int gicv2_do_probe(const void *fdt)
 
        /* Set driver functions */
        gicv2_drv.ops = drv_ops;
+}
+
+#if defined(CONFIG_UKPLAT_ACPI)
+static int acpi_get_gicc(struct _gic_dev *g)
+{
+       union {
+               struct acpi_madt_gicc *gicc;
+               struct acpi_subsdt_hdr *h;
+       } m;
+       struct acpi_madt *madt;
+       __sz off, len;
+
+       madt = acpi_get_madt();
+       UK_ASSERT(madt);
+
+       /* In ACPI all GICCs' base address must be the same */
+       len = madt->hdr.tab_len - sizeof(*madt);
+       for (off = 0; off < len; off += m.h->len) {
+               m.h = (struct acpi_subsdt_hdr *)(madt->entries + off);
+
+               if (m.h->type != ACPI_MADT_GICC)
+                       continue;
+
+               /* If GICv3/4 this field is 0 */
+               if (!m.gicc->paddr)
+                       return -ENOTSUP;
+
+               g->cpuif_mem_addr = m.gicc->paddr;
+               g->cpuif_mem_size = GICC_MEM_SZ;
+
+               return 0;
+       }
+
+       return -ENOENT;
+}
+
+static int gicv2_do_probe(void)
+{
+       int rc;
+
+       rc = acpi_get_gicc(&gicv2_drv);
+       if (unlikely(rc < 0))
+               return rc;
+
+       rc = acpi_get_gicd(&gicv2_drv);
+       if (unlikely(rc < 0))
+               return rc;
+
+       if (unlikely(gicv2_drv.dist_mem_size != GICD_V2_MEM_SZ))
+               return -ENOTSUP;
+
+       return 0;
+}
+#else /* CONFIG_UKPLAT_ACPI */
+static int gicv2_do_probe(void)
+{
+       struct ukplat_bootinfo *bi = ukplat_bootinfo_get();
+       int fdt_gic, r;
+       void *fdt;
+
+       UK_ASSERT(bi);
+       fdt = (void *)bi->dtb;
 
        /* Currently, we only support 1 GIC per system */
        fdt_gic = fdt_node_offset_by_compatible_list(fdt, -1, gic_device_list);
@@ -530,30 +595,19 @@ static int gicv2_do_probe(const void *fdt)
                return r;
        }
 
-       uk_pr_info("Found GICv2 on:\n");
-       uk_pr_info("\tDistributor  : 0x%lx - 0x%lx\n",
-                  gicv2_drv.dist_mem_addr,
-                  gicv2_drv.dist_mem_addr + gicv2_drv.dist_mem_size - 1);
-       uk_pr_info("\tCPU interface: 0x%lx - 0x%lx\n",
-                  gicv2_drv.cpuif_mem_addr,
-                  gicv2_drv.cpuif_mem_addr + gicv2_drv.cpuif_mem_size - 1);
-
-       /* GICv2 is present */
-       gicv2_drv.is_present = 1;
-
        return 0;
 }
+#endif /* !CONFIG_UKPLAT_ACPI */
 
 /**
- * Probe device tree for GICv2
+ * Probe device tree or ACPI for GICv2
  * NOTE: First time must not be called from multiple CPUs in parallel
  *
- * @param [in] fdt pointer to device tree
  * @param [out] dev receives pointer to GICv2 if available, NULL otherwise
  *
  * @return 0 if device is available, an FDT (FDT_ERR_*) error otherwise
  */
-int gicv2_probe(const void *fdt, struct _gic_dev **dev)
+int gicv2_probe(struct _gic_dev **dev)
 {
        int rc;
 
@@ -572,12 +626,24 @@ int gicv2_probe(const void *fdt, struct _gic_dev **dev)
 
        gicv2_drv.is_probed = 1;
 
-       rc = gicv2_do_probe(fdt);
+       rc = gicv2_do_probe();
        if (rc) {
                *dev = NULL;
                return rc;
        }
 
+       uk_pr_info("Found GICv2 on:\n");
+       uk_pr_info("\tDistributor  : 0x%lx - 0x%lx\n",
+                  gicv2_drv.dist_mem_addr,
+                  gicv2_drv.dist_mem_addr + gicv2_drv.dist_mem_size - 1);
+       uk_pr_info("\tCPU interface: 0x%lx - 0x%lx\n",
+                  gicv2_drv.cpuif_mem_addr,
+                  gicv2_drv.cpuif_mem_addr + gicv2_drv.cpuif_mem_size - 1);
+
+       /* GICv2 is present */
+       gicv2_drv.is_present = 1;
+       gicv2_set_ops();
+
        *dev = &gicv2_drv;
        return 0;
 }
index a05cf4682d07d8a7c1a6eaf154d08cfa2a1082e5..4252207a33bb33491ffc64f8e71f1ec8c755e861 100644 (file)
@@ -251,6 +251,11 @@ enum sgi_filter {
  * so we just describe non-secure registers.
  */
 
+/* Default page-aligned up size for GICv2 CPU interface according to
+ * ARM Generic Interrupt Controller Architecture version 2.0 Issue B.b.
+ */
+#define GICC_MEM_SZ    0x2000
+
 /* CPU Interface Control Register */
 #define GICC_CTLR              0x0000
 #define GICC_CTLR_ENABLE       0x1
@@ -311,13 +316,12 @@ void gicv2_sgi_gen_to_others(uint32_t sgintid);
 void gicv2_sgi_gen_to_self(uint32_t sgintid);
 
 /**
- * Probe device tree for GICv2
+ * Probe device tree or ACPI for GICv2
  * NOTE: First time must not be called from multiple CPUs in parallel
  *
- * @param [in] fdt pointer to device tree
  * @param [out] dev receives pointer to GICv2 if available, NULL otherwise
- * @return 0 if device is available, an FDT (FDT_ERR_*) error otherwise
+ * @return 0 if device is available, < 0 otherwise
  */
-int gicv2_probe(const void *fdt, struct _gic_dev **dev);
+int gicv2_probe(struct _gic_dev **dev);
 
 #endif /* __PLAT_DRV_ARM_GICV2_H__ */