arm-trusted-firmware/plat/arm/board/arm_fpga/fpga_bl31_setup.c
Andre Przywara 52b8f4465e refactor(arm_fpga): move command line code into separate function
The code dealing with finding the command line and inserting that into
the DTB is somewhat large, and drowns the other DT handlers in our
fpga_prepare_dtb() function.

Move that code into a separate function, to improve readability.

Change-Id: I828203c4bb248d38a2562fcb6afdefedf3179f8d
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
2021-11-04 15:58:34 +00:00

280 lines
7.1 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/arm/gicv3.h>
#include <drivers/delay_timer.h>
#include <drivers/generic_delay_timer.h>
#include <lib/extensions/spe.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);
}
#define CMDLINE_SIGNATURE "CMD:"
static int fpga_dtb_set_commandline(void *fdt, const char *cmdline)
{
int chosen;
const char *eol;
char nul = 0;
int slen, err;
chosen = fdt_add_subnode(fdt, 0, "chosen");
if (chosen == -FDT_ERR_EXISTS) {
chosen = fdt_path_offset(fdt, "/chosen");
}
if (chosen < 0) {
return chosen;
}
/*
* 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 == NULL) {
eol = strchr(cmdline, 0);
}
/* Skip the signature and omit the EOL/NUL byte. */
slen = eol - (cmdline + strlen(CMDLINE_SIGNATURE));
/*
* 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 + strlen(CMDLINE_SIGNATURE), slen);
if (err != 0) {
return err;
}
return fdt_appendprop(fdt, chosen, "bootargs", &nul, 1);
}
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();
}
/* Reserve memory used by Trusted Firmware. */
if (fdt_add_reserved_memory(fdt, "tf-a@80000000", BL31_BASE,
BL31_LIMIT - BL31_BASE)) {
WARN("Failed to add reserved memory node to DT\n");
}
/* Check for the command line signature. */
if (!strncmp(cmdline, CMDLINE_SIGNATURE, strlen(CMDLINE_SIGNATURE))) {
err = fpga_dtb_set_commandline(fdt, cmdline);
if (err == 0) {
INFO("using command line at 0x%x\n",
FPGA_PRELOADED_CMD_LINE);
} else {
ERROR("failed to put command line into DTB: %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();
} else {
unsigned int nr_cores = fpga_get_nr_gic_cores();
INFO("Adjusting GICR DT region to cover %u cores\n",
nr_cores);
err = fdt_adjust_gic_redist(fdt, nr_cores,
fpga_get_redist_base(),
fpga_get_redist_size());
if (err < 0) {
ERROR("Error %d fixing up GIC DT node\n", err);
}
}
}
/* Check whether we support the SPE PMU. Remove the DT node if not. */
if (!spe_supported()) {
int node = fdt_node_offset_by_compatible(fdt, 0,
"arm,statistical-profiling-extension-v1");
if (node >= 0) {
fdt_del_node(fdt, node);
}
}
/* Check whether we have an ITS. Remove the DT node if not. */
if (!fpga_has_its()) {
int node = fdt_node_offset_by_compatible(fdt, 0,
"arm,gic-v3-its");
if (node >= 0) {
fdt_del_node(fdt, node);
}
}
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 */
}