Commit f240728b authored by Andre Przywara's avatar Andre Przywara
Browse files

qemu: Move and generalise FDT PSCI fixup



The QEMU platform port scans its device tree to advertise PSCI as the
CPU enable method. It does this by scanning *every* node in the DT and
check whether its compatible string starts with "arm,cortex-a". Then it
sets the enable-method to PSCI, if it doesn't already have one.

Other platforms might want to use this functionality as well, so let's
move it out of the QEMU platform directory and make it more robust by
fixing some shortcomings:
- A compatible string starting with a certain prefix is not a good way
to find the CPU nodes. For instance a "arm,cortex-a72-pmu" node will
match as well and is in turn favoured with an enable-method.
- If the DT already has an enable-method, we won't change this to PSCI.

Those two issues will for instance fail on the Raspberry Pi 4 DT.
To fix those problems, we adjust the scanning method:
The DT spec says that all CPU nodes are subnodes of the mandatory
/cpus node, which is a subnode of the root node. Also each CPU node has
to have a device_type = "cpu" property. So we find the /cpus node, then
scan for a subnode with the proper device_type, forcing the
enable-method to "psci".
We have to restart this search after a property has been patched, as the
node offsets might have changed meanwhile.

This allows this routine to be reused for the Raspberry Pi 4 later.

Change-Id: I00cae16cc923d9f8bb96a9b2a2933b9a79b06139
Signed-off-by: default avatarAndre Przywara <andre.przywara@arm.com>
parent 990ab78e
/*
* Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Contains generic routines to fix up the device tree blob passed on to
* payloads like BL32 and BL33 (and further down the boot chain).
* This allows to easily add PSCI nodes, when the original DT does not have
* it or advertises another method.
*/
#include <string.h>
#include <libfdt.h>
......@@ -12,7 +19,7 @@
#include <drivers/console.h>
#include <lib/psci/psci.h>
#include "qemu_private.h"
#include <common/fdt_fixup.h>
static int append_psci_compatible(void *fdt, int offs, const char *str)
{
......@@ -55,45 +62,65 @@ int dt_add_psci_node(void *fdt)
return 0;
}
static int check_node_compat_prefix(void *fdt, int offs, const char *prefix)
/*
* Find the first subnode that has a "device_type" property with the value
* "cpu" and which's enable-method is not "psci" (yet).
* Returns 0 if no such subnode is found, so all have already been patched
* or none have to be patched in the first place.
* Returns 1 if *one* such subnode has been found and successfully changed
* to "psci".
* Returns -1 on error.
*
* Call in a loop until it returns 0. Recalculate the node offset after
* it has returned 1.
*/
static int dt_update_one_cpu_node(void *fdt, int offset)
{
const size_t prefix_len = strlen(prefix);
size_t l;
int plen;
const char *prop;
int offs;
prop = fdt_getprop(fdt, offs, "compatible", &plen);
if (!prop)
return -1;
/* Iterate over all subnodes to find those with device_type = "cpu". */
for (offs = fdt_first_subnode(fdt, offset); offs >= 0;
offs = fdt_next_subnode(fdt, offs)) {
const char *prop;
int len;
while (plen > 0) {
if (memcmp(prop, prefix, prefix_len) == 0)
return 0; /* match */
prop = fdt_getprop(fdt, offs, "device_type", &len);
if (!prop)
continue;
if (memcmp(prop, "cpu", 4) != 0 || len != 4)
continue;
l = strlen(prop) + 1;
prop += l;
plen -= l;
/* Ignore any nodes which already use "psci". */
prop = fdt_getprop(fdt, offs, "enable-method", &len);
if (prop && memcmp(prop, "psci", 5) == 0 && len == 5)
continue;
if (fdt_setprop_string(fdt, offs, "enable-method", "psci"))
return -1;
/*
* Subnode found and patched.
* Restart to accommodate potentially changed offsets.
*/
return 1;
}
return -1;
if (offs == -FDT_ERR_NOTFOUND)
return 0;
return offs;
}
int dt_add_psci_cpu_enable_methods(void *fdt)
{
int offs = 0;
int offs, ret;
while (1) {
offs = fdt_next_node(fdt, offs, NULL);
do {
offs = fdt_path_offset(fdt, "/cpus");
if (offs < 0)
break;
if (fdt_getprop(fdt, offs, "enable-method", NULL))
continue; /* already set */
if (check_node_compat_prefix(fdt, offs, "arm,cortex-a"))
continue; /* no compatible */
if (fdt_setprop_string(fdt, offs, "enable-method", "psci"))
return -1;
/* Need to restart scanning as offsets may have changed */
offs = 0;
}
return 0;
return offs;
ret = dt_update_one_cpu_node(fdt, offs);
} while (ret > 0);
return ret;
}
/*
* Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef FDT_FIXUP_H
#define FDT_FIXUP_H
int dt_add_psci_node(void *fdt);
int dt_add_psci_cpu_enable_methods(void *fdt);
#endif /* FDT_FIXUP_H */
......@@ -114,7 +114,7 @@ BL2_SOURCES += drivers/io/io_semihosting.c \
plat/qemu/qemu_io_storage.c \
plat/qemu/${ARCH}/plat_helpers.S \
plat/qemu/qemu_bl2_setup.c \
plat/qemu/dt.c \
common/fdt_fixup.c \
plat/qemu/qemu_bl2_mem_params_desc.c \
plat/qemu/qemu_image_load.c \
common/desc_image_load.c
......
/*
* Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
......@@ -15,6 +15,7 @@
#include <common/bl_common.h>
#include <common/debug.h>
#include <common/desc_image_load.h>
#include <common/fdt_fixup.h>
#include <lib/optee_utils.h>
#include <lib/utils.h>
#include <plat/common/platform.h>
......
......@@ -28,9 +28,6 @@ void qemu_configure_mmu_el3(unsigned long total_base, unsigned long total_size,
void plat_qemu_io_setup(void);
unsigned int plat_qemu_calc_core_pos(u_register_t mpidr);
int dt_add_psci_node(void *fdt);
int dt_add_psci_cpu_enable_methods(void *fdt);
void qemu_console_init(void);
void plat_qemu_gic_init(void);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment