arm-trusted-firmware/plat/arm/board/arm_fpga/fpga_bl31_setup.c
Javier Almansa Sobrino 20ff991e92 arm_fpga: Add support to populate the CPU nodes in the DTB
At the moment BL31 dynamically discovers the CPU topology of an FPGA
system at runtime, but does not export it to the non-secure world.
Any BL33 user would typically looks at the devicetree to learn about
existing CPUs.

This patch exports a minimum /cpus node in a devicetree to satisfy
the binding. This means that no cpumaps or caches are described.
This could be added later if needed.

An existing /cpus node in the DT will make the code bail out with a
message.

Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Change-Id: I589a2b3412411a3660134bdcef3a65e8200e1d7e
2020-09-02 16:46:48 +01:00

232 lines
5.9 KiB
C

/*
* Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <common/fdt_fixup.h>
#include <common/fdt_wrappers.h>
#include <drivers/delay_timer.h>
#include <drivers/generic_delay_timer.h>
#include <libfdt.h>
#include "fpga_private.h"
#include <plat/common/platform.h>
#include <platform_def.h>
static entry_point_info_t bl33_image_ep_info;
volatile uint32_t secondary_core_spinlock;
uintptr_t plat_get_ns_image_entrypoint(void)
{
#ifdef PRELOADED_BL33_BASE
return PRELOADED_BL33_BASE;
#else
return 0ULL;
#endif
}
uint32_t fpga_get_spsr_for_bl33_entry(void)
{
return SPSR_64(MODE_EL2, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
}
void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
u_register_t arg2, u_register_t arg3)
{
/* Add this core to the VALID mpids list */
fpga_valid_mpids[plat_my_core_pos()] = VALID_MPID;
/*
* Notify the secondary CPUs that the C runtime is ready
* so they can announce themselves.
*/
secondary_core_spinlock = C_RUNTIME_READY_KEY;
dsbish();
sev();
fpga_console_init();
bl33_image_ep_info.pc = plat_get_ns_image_entrypoint();
bl33_image_ep_info.spsr = fpga_get_spsr_for_bl33_entry();
SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE);
/* Set x0-x3 for the primary CPU as expected by the kernel */
bl33_image_ep_info.args.arg0 = (u_register_t)FPGA_PRELOADED_DTB_BASE;
bl33_image_ep_info.args.arg1 = 0U;
bl33_image_ep_info.args.arg2 = 0U;
bl33_image_ep_info.args.arg3 = 0U;
}
void bl31_plat_arch_setup(void)
{
}
void bl31_platform_setup(void)
{
/* Write frequency to CNTCRL and initialize timer */
generic_delay_timer_init();
/*
* Before doing anything else, wait for some time to ensure that
* the secondary CPUs have populated the fpga_valid_mpids array.
* As the number of secondary cores is unknown and can even be 0,
* it is not possible to rely on any signal from them, so use a
* delay instead.
*/
mdelay(5);
/*
* On the event of a cold reset issued by, for instance, a reset pin
* assertion, we cannot guarantee memory to be initialized to zero.
* In such scenario, if the secondary cores reached
* plat_secondary_cold_boot_setup before the primary one initialized
* .BSS, we could end up having a race condition if the spinlock
* was not cleared before.
*
* Similarly, if there were a reset before the spinlock had been
* cleared, the secondary cores would find the lock opened before
* .BSS is cleared, causing another race condition.
*
* So clean the spinlock as soon as we think it is safe to reduce the
* chances of any race condition on a reset.
*/
secondary_core_spinlock = 0UL;
/* Initialize the GIC driver, cpu and distributor interfaces */
plat_fpga_gic_init();
}
entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type)
{
entry_point_info_t *next_image_info;
next_image_info = &bl33_image_ep_info;
/* Only expecting BL33: the kernel will run in EL2NS */
assert(type == NON_SECURE);
/* None of the images can have 0x0 as the entrypoint */
if (next_image_info->pc) {
return next_image_info;
} else {
return NULL;
}
}
unsigned int plat_get_syscnt_freq2(void)
{
const void *fdt = (void *)(uintptr_t)FPGA_PRELOADED_DTB_BASE;
int node;
node = fdt_node_offset_by_compatible(fdt, 0, "arm,armv8-timer");
if (node < 0) {
return FPGA_DEFAULT_TIMER_FREQUENCY;
}
return fdt_read_uint32_default(fdt, node, "clock-frequency",
FPGA_DEFAULT_TIMER_FREQUENCY);
}
static void fpga_prepare_dtb(void)
{
void *fdt = (void *)(uintptr_t)FPGA_PRELOADED_DTB_BASE;
const char *cmdline = (void *)(uintptr_t)FPGA_PRELOADED_CMD_LINE;
int err;
err = fdt_open_into(fdt, fdt, FPGA_MAX_DTB_SIZE);
if (err < 0) {
ERROR("cannot open devicetree at %p: %d\n", fdt, err);
panic();
}
/* Check for the command line signature. */
if (!strncmp(cmdline, "CMD:", 4)) {
int chosen;
INFO("using command line at 0x%x\n", FPGA_PRELOADED_CMD_LINE);
chosen = fdt_add_subnode(fdt, 0, "chosen");
if (chosen == -FDT_ERR_EXISTS) {
chosen = fdt_path_offset(fdt, "/chosen");
}
if (chosen < 0) {
ERROR("cannot find /chosen node: %d\n", chosen);
} else {
const char *eol;
char nul = 0;
int slen;
/*
* There is most likely an EOL at the end of the
* command line, make sure we terminate the line there.
* We can't replace the EOL with a NUL byte in the
* source, as this is in read-only memory. So we first
* create the property without any termination, then
* append a single NUL byte.
*/
eol = strchr(cmdline, '\n');
if (!eol) {
eol = strchr(cmdline, 0);
}
/* Skip the signature and omit the EOL/NUL byte. */
slen = eol - (cmdline + 4);
/*
* Let's limit the size of the property, just in case
* we find the signature by accident. The Linux kernel
* limits to 4096 characters at most (in fact 2048 for
* arm64), so that sounds like a reasonable number.
*/
if (slen > 4095) {
slen = 4095;
}
err = fdt_setprop(fdt, chosen, "bootargs",
cmdline + 4, slen);
if (!err) {
err = fdt_appendprop(fdt, chosen, "bootargs",
&nul, 1);
}
if (err) {
ERROR("Could not set command line: %d\n", err);
}
}
}
if (err < 0) {
ERROR("Error %d extending Device Tree\n", err);
panic();
}
err = fdt_add_cpus_node(fdt, FPGA_MAX_PE_PER_CPU,
FPGA_MAX_CPUS_PER_CLUSTER,
FPGA_MAX_CLUSTER_COUNT);
if (err == -EEXIST) {
WARN("Not overwriting already existing /cpus node in DTB\n");
} else {
if (err < 0) {
ERROR("Error %d creating the /cpus DT node\n", err);
panic();
}
}
err = fdt_pack(fdt);
if (err < 0) {
ERROR("Failed to pack Device Tree at %p: error %d\n", fdt, err);
}
clean_dcache_range((uintptr_t)fdt, fdt_blob_size(fdt));
}
void bl31_plat_runtime_setup(void)
{
fpga_prepare_dtb();
}
void bl31_plat_enable_mmu(uint32_t flags)
{
/* TODO: determine if MMU needs to be enabled */
}