From 0f6197805820a1b62195b6fd7eebef4a1df53da6 Mon Sep 17 00:00:00 2001
From: Simon Shields <simon.shields@data61.csiro.au>
Date: Thu, 12 Dec 2019 17:46:44 +1100
Subject: [PATCH] hardware_gen: add elfloader CPUs output

outputs all CPUs described in the DT to the elfloader
header and also includes any devices in the seL4,elfoader-devices
property.
---
 .../allwinnerA20/overlay-allwinnerA20.dts     |  3 +
 src/plat/am335x/overlay-am335x.dts            |  2 +
 src/plat/apq8064/overlay-apq8064.dts          |  2 +
 src/plat/bcm2837/overlay-rpi3.dts             |  3 +
 src/plat/exynos4/overlay-exynos4.dts          |  3 +
 src/plat/exynos5/overlay-exynos5250.dts       |  2 +
 src/plat/exynos5/overlay-exynos5410.dts       |  2 +
 src/plat/exynos5/overlay-exynos5422.dts       |  2 +
 src/plat/fvp/overlay-fvp.dts                  |  4 +
 src/plat/hikey/overlay-hikey.dts              |  5 +
 src/plat/imx31/overlay-kzm.dts                |  2 +
 src/plat/imx6/overlay-sabre.dts               |  4 +
 src/plat/imx6/overlay-wandq.dts               |  4 +
 src/plat/imx7/overlay-imx7sabre.dts           |  2 +
 src/plat/imx8m-evk/overlay-imx8mm-evk.dts     |  4 +
 src/plat/imx8m-evk/overlay-imx8mq-evk.dts     |  4 +
 src/plat/odroidc2/overlay-odroidc2.dts        |  4 +
 src/plat/omap3/overlay-omap3.dts              |  2 +
 .../qemu-arm-virt/overlay-qemu-arm-virt.dts   |  3 +
 src/plat/rockpro64/overlay-rockpro64.dts      |  3 +
 src/plat/tk1/overlay-tk1.dts                  |  2 +
 src/plat/tx1/overlay-tx1.dts                  |  4 +
 src/plat/tx2/overlay-tx2.dts                  |  3 +
 src/plat/zynq7000/overlay-zynq7000.dts        |  3 +
 src/plat/zynqmp/overlay-zynqmp.dts            |  3 +
 tools/hardware.yml                            |  9 ++
 tools/hardware/device.py                      | 12 ++-
 tools/hardware/fdt.py                         | 12 ++-
 tools/hardware/outputs/elfloader.py           | 93 +++++++++++++++----
 tools/hardware/utils/cpu.py                   | 30 ++++++
 30 files changed, 207 insertions(+), 24 deletions(-)
 create mode 100644 tools/hardware/utils/cpu.py

diff --git a/src/plat/allwinnerA20/overlay-allwinnerA20.dts b/src/plat/allwinnerA20/overlay-allwinnerA20.dts
index a6a58e4d6..0c08583fe 100644
--- a/src/plat/allwinnerA20/overlay-allwinnerA20.dts
+++ b/src/plat/allwinnerA20/overlay-allwinnerA20.dts
@@ -12,6 +12,9 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0";
+
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/soc@1c00000/interrupt-controller@1c81000},
diff --git a/src/plat/am335x/overlay-am335x.dts b/src/plat/am335x/overlay-am335x.dts
index 8cd502add..5427e988d 100644
--- a/src/plat/am335x/overlay-am335x.dts
+++ b/src/plat/am335x/overlay-am335x.dts
@@ -12,6 +12,8 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0";
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/ocp/interrupt-controller@48200000},
diff --git a/src/plat/apq8064/overlay-apq8064.dts b/src/plat/apq8064/overlay-apq8064.dts
index 49b0d1088..60f56fb9b 100644
--- a/src/plat/apq8064/overlay-apq8064.dts
+++ b/src/plat/apq8064/overlay-apq8064.dts
@@ -12,6 +12,8 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0";
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/soc/interrupt-controller@2000000},
diff --git a/src/plat/bcm2837/overlay-rpi3.dts b/src/plat/bcm2837/overlay-rpi3.dts
index e3219d302..5f783209a 100644
--- a/src/plat/bcm2837/overlay-rpi3.dts
+++ b/src/plat/bcm2837/overlay-rpi3.dts
@@ -12,6 +12,9 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial1";
+
 		seL4,kernel-devices =
 		    "serial1",
 		    &{/soc/interrupt-controller@7e00b200},
diff --git a/src/plat/exynos4/overlay-exynos4.dts b/src/plat/exynos4/overlay-exynos4.dts
index ece5d960e..02873cd90 100644
--- a/src/plat/exynos4/overlay-exynos4.dts
+++ b/src/plat/exynos4/overlay-exynos4.dts
@@ -12,6 +12,9 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial1";
+
 		seL4,kernel-devices =
 		    "serial1",
 		    &{/soc/interrupt-controller@10490000},
diff --git a/src/plat/exynos5/overlay-exynos5250.dts b/src/plat/exynos5/overlay-exynos5250.dts
index 510b269a3..009f629ef 100644
--- a/src/plat/exynos5/overlay-exynos5250.dts
+++ b/src/plat/exynos5/overlay-exynos5250.dts
@@ -14,6 +14,8 @@
 	/* Pick serial console */
 	chosen {
 		stdout-path = "serial2:115200n8";
+		seL4,elfloader-devices =
+		    "serial2";
 		seL4,kernel-devices =
 		    "serial2",
 		    &{/soc/interrupt-controller@10481000},
diff --git a/src/plat/exynos5/overlay-exynos5410.dts b/src/plat/exynos5/overlay-exynos5410.dts
index d673defd5..6c75a42ec 100644
--- a/src/plat/exynos5/overlay-exynos5410.dts
+++ b/src/plat/exynos5/overlay-exynos5410.dts
@@ -14,6 +14,8 @@
 	/* pick the right boot CPU */
 	chosen {
 		seL4,boot-cpu = <&{/cpus/cpu@0}>;
+		seL4,elfloader-devices =
+		    "serial2";
 		seL4,kernel-devices =
 		    "serial2",
 		    &{/soc/interrupt-controller@10481000},
diff --git a/src/plat/exynos5/overlay-exynos5422.dts b/src/plat/exynos5/overlay-exynos5422.dts
index 46999dee2..04643b6c2 100644
--- a/src/plat/exynos5/overlay-exynos5422.dts
+++ b/src/plat/exynos5/overlay-exynos5422.dts
@@ -20,6 +20,8 @@
 	 */
 	chosen {
 		seL4,boot-cpu = <&{/cpus/cpu@100}>;
+		seL4,elfloader-devices =
+		    "serial2";
 		seL4,kernel-devices =
 		    "serial2",
 		    &{/soc/interrupt-controller@10481000},
diff --git a/src/plat/fvp/overlay-fvp.dts b/src/plat/fvp/overlay-fvp.dts
index 61fbc2619..e213b163e 100644
--- a/src/plat/fvp/overlay-fvp.dts
+++ b/src/plat/fvp/overlay-fvp.dts
@@ -12,6 +12,10 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0",
+		    &{/psci};
+
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/interrupt-controller@2f000000},
diff --git a/src/plat/hikey/overlay-hikey.dts b/src/plat/hikey/overlay-hikey.dts
index 89b41726e..f764f10ac 100644
--- a/src/plat/hikey/overlay-hikey.dts
+++ b/src/plat/hikey/overlay-hikey.dts
@@ -13,6 +13,11 @@
 / {
 	chosen {
 		stdout-path = "serial0:115200n8";
+
+		seL4,elfloader-devices =
+		    "serial0",
+		    &{/psci};
+
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/interrupt-controller@f6801000},
diff --git a/src/plat/imx31/overlay-kzm.dts b/src/plat/imx31/overlay-kzm.dts
index 96d37bf7e..a8b7ba7b2 100644
--- a/src/plat/imx31/overlay-kzm.dts
+++ b/src/plat/imx31/overlay-kzm.dts
@@ -14,6 +14,8 @@
 	/* Pick serial console */
 	chosen {
 		stdout-path = "serial0";
+		seL4,elfloader-devices =
+		    "serial0";
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/interrupt-controller@68000000},
diff --git a/src/plat/imx6/overlay-sabre.dts b/src/plat/imx6/overlay-sabre.dts
index 5e4d63eea..f6e799b9c 100644
--- a/src/plat/imx6/overlay-sabre.dts
+++ b/src/plat/imx6/overlay-sabre.dts
@@ -12,6 +12,10 @@
 
 / {
 	chosen {
+               seL4,elfloader-devices =
+                   "serial1",
+                   &{/soc/aips-bus@2000000/src@20d8000};
+
 		seL4,kernel-devices =
 		    "serial1",
 		    &{/soc/interrupt-controller@a01000},
diff --git a/src/plat/imx6/overlay-wandq.dts b/src/plat/imx6/overlay-wandq.dts
index 6952a9243..ad74db66d 100644
--- a/src/plat/imx6/overlay-wandq.dts
+++ b/src/plat/imx6/overlay-wandq.dts
@@ -12,6 +12,10 @@
 
 / {
 	chosen {
+               seL4,elfloader-devices =
+                   "serial0",
+                   &{/soc/aips-bus@2000000/src@20d8000};
+
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/soc/interrupt-controller@a01000},
diff --git a/src/plat/imx7/overlay-imx7sabre.dts b/src/plat/imx7/overlay-imx7sabre.dts
index c910b0d04..19e33a2c5 100644
--- a/src/plat/imx7/overlay-imx7sabre.dts
+++ b/src/plat/imx7/overlay-imx7sabre.dts
@@ -12,6 +12,8 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0";
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/soc/interrupt-controller@31001000},
diff --git a/src/plat/imx8m-evk/overlay-imx8mm-evk.dts b/src/plat/imx8m-evk/overlay-imx8mm-evk.dts
index abc2596fa..fe553eaf3 100644
--- a/src/plat/imx8m-evk/overlay-imx8mm-evk.dts
+++ b/src/plat/imx8m-evk/overlay-imx8mm-evk.dts
@@ -12,6 +12,10 @@
 
 / {
     chosen {
+        seL4,elfloader-devices =
+            "serial1",
+            &{/psci};
+
         seL4,kernel-devices =
             "serial1",
             &{/interrupt-controller@38800000},
diff --git a/src/plat/imx8m-evk/overlay-imx8mq-evk.dts b/src/plat/imx8m-evk/overlay-imx8mq-evk.dts
index 0ef1fe5ff..5bd284d4d 100644
--- a/src/plat/imx8m-evk/overlay-imx8mq-evk.dts
+++ b/src/plat/imx8m-evk/overlay-imx8mq-evk.dts
@@ -12,6 +12,10 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+			"serial0",
+			&{/psci};
+
 		seL4,kernel-devices =
 			"serial0",
 			&{/interrupt-controller@38800000},
diff --git a/src/plat/odroidc2/overlay-odroidc2.dts b/src/plat/odroidc2/overlay-odroidc2.dts
index d00b10763..72efd7375 100644
--- a/src/plat/odroidc2/overlay-odroidc2.dts
+++ b/src/plat/odroidc2/overlay-odroidc2.dts
@@ -12,6 +12,10 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0",
+		    &{/psci};
+
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/soc/interrupt-controller@c4301000},
diff --git a/src/plat/omap3/overlay-omap3.dts b/src/plat/omap3/overlay-omap3.dts
index d80883782..725a2fa30 100644
--- a/src/plat/omap3/overlay-omap3.dts
+++ b/src/plat/omap3/overlay-omap3.dts
@@ -13,6 +13,8 @@
 / {
 	chosen {
 		stdout-path = "serial2";
+		seL4,elfloader-devices =
+		    "serial2";
 		seL4,kernel-devices =
 		    "serial2",
 		    &{/ocp@68000000/interrupt-controller@48200000},
diff --git a/src/plat/qemu-arm-virt/overlay-qemu-arm-virt.dts b/src/plat/qemu-arm-virt/overlay-qemu-arm-virt.dts
index 9fa1e5ab1..176487bcd 100644
--- a/src/plat/qemu-arm-virt/overlay-qemu-arm-virt.dts
+++ b/src/plat/qemu-arm-virt/overlay-qemu-arm-virt.dts
@@ -12,6 +12,9 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    &{/pl011@9000000},
+		    &{/psci};
 		seL4,kernel-devices =
             &{/pl011@9000000},
 		    &{/intc@8000000},
diff --git a/src/plat/rockpro64/overlay-rockpro64.dts b/src/plat/rockpro64/overlay-rockpro64.dts
index 388dd3aa8..046ed72de 100644
--- a/src/plat/rockpro64/overlay-rockpro64.dts
+++ b/src/plat/rockpro64/overlay-rockpro64.dts
@@ -12,6 +12,9 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial2",
+		    &{/psci};
 		seL4,kernel-devices =
 		    "serial2",
 		    &{/interrupt-controller@fee00000},
diff --git a/src/plat/tk1/overlay-tk1.dts b/src/plat/tk1/overlay-tk1.dts
index 64c1bb73e..e2d71aa37 100644
--- a/src/plat/tk1/overlay-tk1.dts
+++ b/src/plat/tk1/overlay-tk1.dts
@@ -12,6 +12,8 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0";
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/interrupt-controller@50041000},
diff --git a/src/plat/tx1/overlay-tx1.dts b/src/plat/tx1/overlay-tx1.dts
index 733aec786..f4d47b59e 100644
--- a/src/plat/tx1/overlay-tx1.dts
+++ b/src/plat/tx1/overlay-tx1.dts
@@ -12,6 +12,10 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0",
+		    &{/psci};
+
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/interrupt-controller@50041000},
diff --git a/src/plat/tx2/overlay-tx2.dts b/src/plat/tx2/overlay-tx2.dts
index 8d2ca5286..d375d7074 100644
--- a/src/plat/tx2/overlay-tx2.dts
+++ b/src/plat/tx2/overlay-tx2.dts
@@ -14,6 +14,9 @@
 	/* seL4 on the TX2 boots on the first non-NVIDIA core */
 	chosen {
 		seL4,boot-cpu = <&{/cpus/cpu@2}>;
+		seL4,elfloader-devices =
+		    "serial0",
+		    &{/psci};
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/interrupt-controller@3881000},
diff --git a/src/plat/zynq7000/overlay-zynq7000.dts b/src/plat/zynq7000/overlay-zynq7000.dts
index 7eae7b894..7896543a9 100644
--- a/src/plat/zynq7000/overlay-zynq7000.dts
+++ b/src/plat/zynq7000/overlay-zynq7000.dts
@@ -12,6 +12,9 @@
 
 / {
 	chosen {
+		seL4,elfloader-devices =
+		    "serial0",
+		    &{/amba/slcr@f8000000/rstc@200};
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/amba/interrupt-controller@f8f01000},
diff --git a/src/plat/zynqmp/overlay-zynqmp.dts b/src/plat/zynqmp/overlay-zynqmp.dts
index 00b276df2..48842af23 100644
--- a/src/plat/zynqmp/overlay-zynqmp.dts
+++ b/src/plat/zynqmp/overlay-zynqmp.dts
@@ -12,6 +12,9 @@
 
 / {
     chosen {
+		seL4,elfloader-devices =
+		    "serial0",
+		    &{/psci};
 		seL4,kernel-devices =
 		    "serial0",
 		    &{/amba_apu@0/interrupt-controller@f9010000},
diff --git a/tools/hardware.yml b/tools/hardware.yml
index fbe210fa0..ee63d946b 100644
--- a/tools/hardware.yml
+++ b/tools/hardware.yml
@@ -238,3 +238,12 @@ devices:
     regions:
       - index: 0
         kernel: PLIC_PPTR
+
+  # elfloader rules
+  - compatible:
+      - arm,psci-0.2
+      - arm,psci-1.0
+  - compatible:
+      - fsl,imx6q-src
+  - compatible:
+      - xlnx,zynq-reset
diff --git a/tools/hardware/device.py b/tools/hardware/device.py
index 365504aa5..f6ce9ec07 100644
--- a/tools/hardware/device.py
+++ b/tools/hardware/device.py
@@ -113,6 +113,11 @@ class WrappedNode:
             reg.append(Region(self.parent._translate_child_address(r[0]), r[1], self))
         return reg
 
+    def parse_address(self, array) -> int:
+        ''' parse a single address from the array. will pop values from the array '''
+        size = self.parent.get_addr_cells()
+        return Utils.make_number(size, array)
+
     def get_interrupts(self, tree: 'FdtParser') -> List[int]:
         irqs = []
         if 'interrupts-extended' in self.props:
@@ -135,9 +140,12 @@ class WrappedNode:
 
     def visit(self, visitor: Any):
         ''' Visit this node and all its children '''
-        visitor(self)
+        ret = [visitor(self)]
+        if ret[0] is None:
+            ret = []
         for child in self.children.values():
-            child.visit(visitor)
+            ret += child.visit(visitor)
+        return ret
 
     def __iter__(self) -> Generator['WrappedNode', None, None]:
         ''' Iterate over all immediate children of this node '''
diff --git a/tools/hardware/fdt.py b/tools/hardware/fdt.py
index 75a3f7704..aa6b50dac 100644
--- a/tools/hardware/fdt.py
+++ b/tools/hardware/fdt.py
@@ -79,13 +79,19 @@ class FdtParser:
         return prop.strings[0]
 
     def get_kernel_devices(self) -> List[WrappedNode]:
+        return self.get_devices_list('seL4,kernel-devices')
+
+    def get_elfloader_devices(self) -> List[WrappedNode]:
+        return self.get_devices_list('seL4,elfloader-devices')
+
+    def get_devices_list(self, prop) -> List[WrappedNode]:
         ''' Returns a list of devices that are used by the kernel '''
         chosen = self.get_path('/chosen')
-        if not chosen.has_prop('seL4,kernel-devices'):
+        if not chosen.has_prop(prop):
             return []
 
         ret = []
-        paths = chosen.get_prop('seL4,kernel-devices').strings
+        paths = chosen.get_prop(prop).strings
 
         for path in paths:
             if path[0] != '/':
@@ -104,4 +110,4 @@ class FdtParser:
 
     def visit(self, visitor: Any):
         ''' Walk the tree, calling the given visitor function on each node '''
-        self.wrapped_root.visit(visitor)
+        return self.wrapped_root.visit(visitor)
diff --git a/tools/hardware/outputs/elfloader.py b/tools/hardware/outputs/elfloader.py
index cabfc95b9..10a994b25 100644
--- a/tools/hardware/outputs/elfloader.py
+++ b/tools/hardware/outputs/elfloader.py
@@ -18,9 +18,10 @@ import logging
 import pyfdt.pyfdt
 
 from jinja2 import Environment, BaseLoader
+from typing import List
 
-from hardware import config, fdt
-from hardware.utils import memory, rule
+from hardware import config, device, fdt
+from hardware.utils import cpu, memory, rule
 
 
 HEADER_TEMPLATE = '''/*
@@ -40,6 +41,11 @@ HEADER_TEMPLATE = '''/*
 
 #pragma once
 
+#include <types.h>
+{% if uses_psci %}
+#include <psci.h>
+{% endif %}
+
 #define MAX_NUM_REGIONS {{ max_reg }}
 
 struct elfloader_driver;
@@ -50,6 +56,13 @@ struct elfloader_device {
     struct elfloader_driver *drv;
 };
 
+struct elfloader_cpu {
+    const char *compat;
+    const char *enable_method;
+    word_t cpu_id;
+    word_t extra_data;
+};
+
 #ifdef DRIVER_COMMON
 struct elfloader_device elfloader_devices[] = {
 {% for d in devices %}
@@ -77,33 +90,73 @@ struct elfloader_device elfloader_devices[] = {
     },
 {% endif %}
 };
+
+struct elfloader_cpu elfloader_cpus[] = {
+    {% for cpu in cpus %}
+    {
+        /* {{ cpu['path'] }} */
+        .compat = "{{ cpu['compat'] }}",
+        .enable_method = {{ '"{}"'.format(cpu['enable_method']) if cpu['enable_method'] else 'NULL' }},
+        .cpu_id = {{ "0x{:x}".format(cpu['cpuid']) }},
+        .extra_data = {{ cpu['extra'] }}
+    },
+    {% endfor %}
+    { .compat = NULL /* sentinel */ },
+};
 #else
-struct elfloader_device *elfloader_devices;
+extern struct elfloader_device elfloader_devices[];
+extern struct elfloader_cpu elfloader_cpus[];
 #endif
 '''
 
 
-def get_elfloader_devices(tree: fdt.FdtParser, rules: rule.HardwareYaml):
-    devices = []
+def get_elfloader_cpus(tree: fdt.FdtParser, devices: List[device.WrappedNode]) -> List[dict]:
+    cpus = cpu.get_cpus(tree)
+    PSCI_COMPAT = ['arm,psci-0.2', 'arm,psci-1.0']
+    psci_node = [n for n in devices if n.has_prop('compatible')
+                 and n.get_prop('compatible').strings[0] in PSCI_COMPAT]
 
-    # serial device
-    chosen = tree.get_path('/chosen')
-    if not chosen.has_prop('stdout-path') or type(chosen.get_prop('stdout-path')) != pyfdt.pyfdt.FdtPropertyStrings:
-        logging.info('DT has no stdout-path, elfloader may not produce output!')
+    if len(psci_node) > 0:
+        psci_node = psci_node[0]
     else:
-        val = chosen.get_prop('stdout-path').strings[0]
-        if val[0] == '/':
-            path = val
-        else:
-            path = tree.lookup_alias(val)
-        devices.append(tree.get_path(path))
-    return devices
+        psci_node = None
+
+    cpu_info = []
+    for i, cpu_node in enumerate(sorted(cpus, key=lambda a: a.path)):
+        enable_method = None
+        if cpu_node.has_prop('enable-method'):
+            enable_method = cpu_node.get_prop('enable-method').strings[0]
+
+        cpuid = i
+        if cpu_node.has_prop('reg'):
+            cpuid = cpu_node.parse_address(list(cpu_node.get_prop('reg').words))
+
+        extra_data = 0
+        if enable_method == 'psci' and psci_node:
+            extra_data = 'PSCI_METHOD_' + psci_node.get_prop('method').strings[0].upper()
+        elif enable_method == 'spin-table':
+            extra_data = '0x{:x}'.format(
+                device.Utils.make_number(2, list(cpu_node.get_prop('cpu-release-addr').words)))
+
+        obj = {
+            'compat': cpu_node.get_prop('compatible').strings[0],
+            'enable_method': enable_method,
+            'cpuid': cpuid,
+            'path': cpu_node.path,
+            'extra': extra_data,
+        }
+        cpu_info.append(obj)
+
+    # guarantee that cpus in the same cluster will be consecutive
+    return sorted(cpu_info, key=lambda a: a['cpuid'])
 
 
 def run(tree: fdt.FdtParser, hardware: rule.HardwareYaml, config: config.Config, args: argparse.Namespace):
-    devices = get_elfloader_devices(tree, hardware)
-    device_info = []
+    devices = tree.get_elfloader_devices()
+    cpu_info = get_elfloader_cpus(tree, devices)
+
     max_reg = 1
+    device_info = []
     for dev in devices:
         obj = {
             'compat': hardware.get_matched_compatible(dev),
@@ -120,8 +173,10 @@ def run(tree: fdt.FdtParser, hardware: rule.HardwareYaml, config: config.Config,
                            lstrip_blocks=True).from_string(HEADER_TEMPLATE)
 
     template_args = dict(builtins.__dict__, **{
+        'cpus': cpu_info,
         'devices': device_info,
-        'max_reg': max_reg
+        'max_reg': max_reg,
+        'uses_psci': any([c['enable_method'] == 'psci' for c in cpu_info])
     })
 
     data = template.render(template_args)
diff --git a/tools/hardware/utils/cpu.py b/tools/hardware/utils/cpu.py
new file mode 100644
index 000000000..5795e8e0c
--- /dev/null
+++ b/tools/hardware/utils/cpu.py
@@ -0,0 +1,30 @@
+#
+# Copyright 2019, Data61
+# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
+# ABN 41 687 119 230.
+#
+# This software may be distributed and modified according to the terms of
+# the GNU General Public License version 2. Note that NO WARRANTY is provided.
+# See "LICENSE_GPLv2.txt" for details.
+#
+# @TAG(DATA61_GPL)
+#
+from typing import List
+
+from hardware.device import WrappedNode
+from hardware.fdt import FdtParser
+
+# documentation for CPU bindings:
+# https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/cpus.yaml
+
+
+def get_cpus(tree: FdtParser) -> List[WrappedNode]:
+    ' Return a list of all the CPUs described in this device tree. '
+    cpus_node = tree.get_path('/cpus')
+
+    found_cpus = []
+    for node in cpus_node:
+        if node.has_prop('device_type') and node.get_prop('device_type').strings[0] == 'cpu':
+            found_cpus.append(node)
+
+    return found_cpus
-- 
GitLab