diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:13:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:13:47 +0000 |
commit | 102b0d2daa97dae68d3eed54d8fe37a9cc38a892 (patch) | |
tree | bcf648efac40ca6139842707f0eba5a4496a6dd2 /plat/arm/board/arm_fpga | |
parent | Initial commit. (diff) | |
download | arm-trusted-firmware-upstream/2.8.0+dfsg.tar.xz arm-trusted-firmware-upstream/2.8.0+dfsg.zip |
Adding upstream version 2.8.0+dfsg.upstream/2.8.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plat/arm/board/arm_fpga')
-rw-r--r-- | plat/arm/board/arm_fpga/aarch64/fpga_helpers.S | 167 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/build_axf.ld.S | 53 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_bl31_setup.c | 402 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_console.c | 38 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_def.h | 39 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_gicv3.c | 162 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_pm.c | 103 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_private.h | 34 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/fpga_topology.c | 77 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/include/plat_macros.S | 13 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/include/platform_def.h | 88 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/kernel_trampoline.S | 35 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/platform.mk | 130 | ||||
-rw-r--r-- | plat/arm/board/arm_fpga/rom_trampoline.S | 24 |
14 files changed, 1365 insertions, 0 deletions
diff --git a/plat/arm/board/arm_fpga/aarch64/fpga_helpers.S b/plat/arm/board/arm_fpga/aarch64/fpga_helpers.S new file mode 100644 index 0000000..20120c9 --- /dev/null +++ b/plat/arm/board/arm_fpga/aarch64/fpga_helpers.S @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <common/bl_common.h> +#include "../fpga_private.h" + +#include <platform_def.h> + + .globl plat_get_my_entrypoint + .globl plat_secondary_cold_boot_setup + .globl plat_is_my_cpu_primary + .globl platform_mem_init + .globl plat_my_core_pos + .globl plat_crash_console_init + .globl plat_crash_console_putc + .globl plat_crash_console_flush + .globl plat_fpga_calc_core_pos + +/* ----------------------------------------------------------------------- + * Indicate a cold boot for every CPU - warm boot is unsupported for the + * holding pen PSCI implementation. + * ----------------------------------------------------------------------- + */ +func plat_get_my_entrypoint + mov x0, #0 + ret +endfunc plat_get_my_entrypoint + +/* ----------------------------------------------------------------------- + * void plat_secondary_cold_boot_setup (void); + * ----------------------------------------------------------------------- + */ +func plat_secondary_cold_boot_setup + + /* + * Wait for the primary processor to initialise the .BSS segment + * to avoid a race condition that would erase fpga_valid_mpids + * if it is populated before the C runtime is ready. + * + * We cannot use the current spin-lock implementation until the + * runtime is up and we should not rely on sevl/wfe instructions as + * it is optional whether they are implemented or not, so we use + * a global variable as lock and wait for the primary processor to + * finish the C runtime bring-up. + */ + + ldr w0, =C_RUNTIME_READY_KEY + adrp x1, secondary_core_spinlock + add x1, x1, :lo12:secondary_core_spinlock +1: + wfe + ldr w2, [x1] + cmp w2, w0 + b.ne 1b + /* Prevent reordering of the store into fpga_valid_mpids below */ + dmb ish + + mov x10, x30 + bl plat_my_core_pos + mov x30, x10 + + adrp x4, fpga_valid_mpids + add x4, x4, :lo12:fpga_valid_mpids + mov x5, #VALID_MPID + strb w5, [x4, x0] + + /* + * Poll the CPU's hold entry until it indicates to jump + * to the entrypoint address. + */ + + adrp x1, hold_base + add x1, x1, :lo12:hold_base +poll_hold_entry: + ldr x3, [x1, x0, LSL #PLAT_FPGA_HOLD_ENTRY_SHIFT] + cmp x3, #PLAT_FPGA_HOLD_STATE_GO + b.ne 1f + + adrp x2, fpga_sec_entrypoint + add x2, x2, :lo12:fpga_sec_entrypoint + ldr x3, [x2] + br x3 +1: + wfe + b poll_hold_entry + +endfunc plat_secondary_cold_boot_setup + +/* ----------------------------------------------------------------------- + * unsigned int plat_is_my_cpu_primary (void); + * + * Find out whether the current cpu is the primary cpu + * ----------------------------------------------------------------------- + */ +func plat_is_my_cpu_primary + mrs x0, mpidr_el1 + mov_imm x1, MPIDR_AFFINITY_MASK + and x0, x0, x1 + cmp x0, #FPGA_PRIMARY_CPU + cset w0, eq + ret +endfunc plat_is_my_cpu_primary + +func platform_mem_init + ret +endfunc platform_mem_init + +func plat_my_core_pos + ldr x1, =(MPID_MASK & ~(MPIDR_AFFLVL_MASK << MPIDR_AFF3_SHIFT)) + mrs x0, mpidr_el1 + and x0, x0, x1 + b plat_fpga_calc_core_pos + +endfunc plat_my_core_pos + +/* ----------------------------------------------------------------------- + * unsigned int plat_fpga_calc_core_pos (uint32_t mpid) + * Clobber registers: x0 to x5 + * ----------------------------------------------------------------------- + */ +func plat_fpga_calc_core_pos + /* + * Check for MT bit in MPIDR, which may be either value for images + * running on the FPGA. + * + * If not set, shift MPIDR to left to make it look as if in a + * multi-threaded implementation. + * + */ + tst x0, #MPIDR_MT_MASK + lsl x3, x0, #MPIDR_AFFINITY_BITS + csel x3, x3, x0, eq + + /* Extract individual affinity fields from MPIDR */ + ubfx x0, x3, #MPIDR_AFF0_SHIFT, #MPIDR_AFFINITY_BITS + ubfx x1, x3, #MPIDR_AFF1_SHIFT, #MPIDR_AFFINITY_BITS + ubfx x2, x3, #MPIDR_AFF2_SHIFT, #MPIDR_AFFINITY_BITS + + mov x4, #FPGA_MAX_CPUS_PER_CLUSTER + mov x5, #FPGA_MAX_PE_PER_CPU + + /* Compute linear position */ + madd x1, x2, x4, x1 + madd x0, x1, x5, x0 + + ret +endfunc plat_fpga_calc_core_pos + +func plat_crash_console_init + mov_imm x0, PLAT_FPGA_CRASH_UART_BASE + b console_pl011_core_init +endfunc plat_crash_console_init + +func plat_crash_console_putc + mov_imm x1, PLAT_FPGA_CRASH_UART_BASE + b console_pl011_core_putc +endfunc plat_crash_console_putc + +func plat_crash_console_flush + mov_imm x0, PLAT_FPGA_CRASH_UART_BASE + b console_pl011_core_flush +endfunc plat_crash_console_flush diff --git a/plat/arm/board/arm_fpga/build_axf.ld.S b/plat/arm/board/arm_fpga/build_axf.ld.S new file mode 100644 index 0000000..d8254e5 --- /dev/null +++ b/plat/arm/board/arm_fpga/build_axf.ld.S @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Linker script for the Arm Ltd. FPGA boards to generate an ELF file that + * contains the ROM trampoline, BL31 and the DTB. + * + * This allows to pass just one file to the uploader tool, and automatically + * provides the correct load addresses. + */ + +#include <platform_def.h> + +OUTPUT_FORMAT("elf64-littleaarch64") +OUTPUT_ARCH(aarch64) + +INPUT(./rom_trampoline.o) +INPUT(./kernel_trampoline.o) + +TARGET(binary) +INPUT(./bl31.bin) +INPUT(./fdts/arm_fpga.dtb) + +ENTRY(_start) + +SECTIONS +{ + .rom (0x0): { + *rom_trampoline.o(.text*) + KEEP(*(.rom)) + } + + .bl31 (BL31_BASE): { + ASSERT(. == ALIGN(PAGE_SIZE), "BL31_BASE is not page aligned"); + *bl31.bin + } + + .dtb (FPGA_PRELOADED_DTB_BASE): { + ASSERT(. == ALIGN(8), "DTB address is not 8-byte aligned"); + *arm_fpga.dtb + } + + .kern_tramp (PRELOADED_BL33_BASE): { + *kernel_trampoline.o(.text*) + KEEP(*(.kern_tramp)) + } + + /DISCARD/ : { *(stacks) } + /DISCARD/ : { *(.debug_*) } + /DISCARD/ : { *(.note*) } + /DISCARD/ : { *(.comment*) } +} diff --git a/plat/arm/board/arm_fpga/fpga_bl31_setup.c b/plat/arm/board/arm_fpga/fpga_bl31_setup.c new file mode 100644 index 0000000..e1b3abb --- /dev/null +++ b/plat/arm/board/arm_fpga/fpga_bl31_setup.c @@ -0,0 +1,402 @@ +/* + * 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 <lib/mmio.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; +static unsigned int system_freq; +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; + } +} + +/* + * Even though we sell the FPGA UART as an SBSA variant, it is actually + * a full fledged PL011. So the baudrate divider registers exist. + */ +#ifndef UARTIBRD +#define UARTIBRD 0x024 +#define UARTFBRD 0x028 +#endif + +/* Round an integer to the closest multiple of a value. */ +static unsigned int round_multiple(unsigned int x, unsigned int multiple) +{ + if (multiple < 2) { + return x; + } + + return ((x + (multiple / 2 - 1)) / multiple) * multiple; +} + +#define PL011_FRAC_SHIFT 6 +#define FPGA_DEFAULT_BAUDRATE 38400 +#define PL011_OVERSAMPLING 16 +static unsigned int pl011_freq_from_divider(unsigned int divider) +{ + unsigned int freq; + + freq = divider * FPGA_DEFAULT_BAUDRATE * PL011_OVERSAMPLING; + + return freq >> PL011_FRAC_SHIFT; +} + +/* + * The FPGAs run most peripherals from one main clock, among them the CPUs, + * the arch timer, and the UART baud base clock. + * The SCP knows this frequency and programs the UART clock divider for a + * 38400 bps baudrate. Recalculate the base input clock from there. + */ +static unsigned int fpga_get_system_frequency(void) +{ + const void *fdt = (void *)(uintptr_t)FPGA_PRELOADED_DTB_BASE; + int node, err; + + /* + * If the arch timer DT node has an explicit clock-frequency property + * set, use that, to allow people overriding auto-detection. + */ + node = fdt_node_offset_by_compatible(fdt, 0, "arm,armv8-timer"); + if (node >= 0) { + uint32_t freq; + + err = fdt_read_uint32(fdt, node, "clock-frequency", &freq); + if (err >= 0) { + return freq; + } + } + + node = fdt_node_offset_by_compatible(fdt, 0, "arm,pl011"); + if (node >= 0) { + uintptr_t pl011_base; + unsigned int divider; + + err = fdt_get_reg_props_by_index(fdt, node, 0, + &pl011_base, NULL); + if (err >= 0) { + divider = mmio_read_32(pl011_base + UARTIBRD); + divider <<= PL011_FRAC_SHIFT; + divider += mmio_read_32(pl011_base + UARTFBRD); + + /* + * The result won't be exact, due to rounding errors, + * but the input frequency was a multiple of 250 KHz. + */ + return round_multiple(pl011_freq_from_divider(divider), + 250000); + } else { + WARN("Cannot read PL011 MMIO base\n"); + } + } else { + WARN("No PL011 DT node\n"); + } + + /* No PL011 DT node or calculation failed. */ + return FPGA_DEFAULT_TIMER_FREQUENCY; +} + +unsigned int plat_get_syscnt_freq2(void) +{ + if (system_freq == 0U) { + system_freq = fpga_get_system_frequency(); + } + + return system_freq; +} + +static void fpga_dtb_update_clock(void *fdt, unsigned int freq) +{ + uint32_t freq_dtb = fdt32_to_cpu(freq); + uint32_t phandle; + int node, err; + + node = fdt_node_offset_by_compatible(fdt, 0, "arm,pl011"); + if (node < 0) { + WARN("%s(): No PL011 DT node found\n", __func__); + + return; + } + + err = fdt_read_uint32(fdt, node, "clocks", &phandle); + if (err != 0) { + WARN("Cannot find clocks property\n"); + + return; + } + + node = fdt_node_offset_by_phandle(fdt, phandle); + if (node < 0) { + WARN("Cannot get phandle\n"); + + return; + } + + err = fdt_setprop_inplace(fdt, node, + "clock-frequency", + &freq_dtb, + sizeof(freq_dtb)); + if (err < 0) { + WARN("Could not update DT baud clock frequency\n"); + + return; + } +} + +#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); + } + } + } + + fpga_dtb_update_clock(fdt, system_freq); + + /* 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 */ +} diff --git a/plat/arm/board/arm_fpga/fpga_console.c b/plat/arm/board/arm_fpga/fpga_console.c new file mode 100644 index 0000000..8c1da62 --- /dev/null +++ b/plat/arm/board/arm_fpga/fpga_console.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> +#include <stdint.h> + +#include <common/fdt_wrappers.h> +#include <drivers/arm/pl011.h> +#include <drivers/console.h> + +#include <platform_def.h> + +static console_t console; + +void fpga_console_init(void) +{ + const void *fdt = (void *)(uintptr_t)FPGA_PRELOADED_DTB_BASE; + uintptr_t base_addr = PLAT_FPGA_CRASH_UART_BASE; + int node; + + /* + * Try to read the UART base address from the DT, by chasing the + * stdout-path property of the chosen node. + * If this does not work, use the crash console address as a fallback. + */ + node = fdt_get_stdout_node_offset(fdt); + if (node >= 0) { + fdt_get_reg_props_by_index(fdt, node, 0, &base_addr, NULL); + } + + (void)console_pl011_register(base_addr, 0, 0, &console); + + console_set_scope(&console, CONSOLE_FLAG_BOOT | + CONSOLE_FLAG_RUNTIME); +} diff --git a/plat/arm/board/arm_fpga/fpga_def.h b/plat/arm/board/arm_fpga/fpga_def.h new file mode 100644 index 0000000..2884ea6 --- /dev/null +++ b/plat/arm/board/arm_fpga/fpga_def.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <lib/utils_def.h> + +#ifndef FPGA_DEF_H +#define FPGA_DEF_H + +/* + * These are set to large values to account for images describing systems with + * larger cluster configurations. + * + * For cases where the number of clusters, cores or threads is smaller than a + * maximum value below, this does not affect the PSCI functionality as any PEs + * that are present will still be indexed appropriately regardless of any empty + * entries in the array used to represent the topology. + */ + +#define FPGA_MAX_CLUSTER_COUNT 4 +#define FPGA_MAX_CPUS_PER_CLUSTER 8 +#define FPGA_MAX_PE_PER_CPU 4 + +#define FPGA_PRIMARY_CPU 0x0 +/******************************************************************************* + * FPGA image memory map related constants + ******************************************************************************/ + +/* + * UART base address, just for the crash console, as a fallback. + * The actual console UART address is taken from the DT. + */ +#define PLAT_FPGA_CRASH_UART_BASE 0x7ff80000 + +#define FPGA_DEFAULT_TIMER_FREQUENCY 10000000 + +#endif diff --git a/plat/arm/board/arm_fpga/fpga_gicv3.c b/plat/arm/board/arm_fpga/fpga_gicv3.c new file mode 100644 index 0000000..e06a9da --- /dev/null +++ b/plat/arm/board/arm_fpga/fpga_gicv3.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2020-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <common/fdt_wrappers.h> +#include <drivers/arm/arm_gicv3_common.h> +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv3.h> +#include <lib/mmio.h> +#include <libfdt.h> + +#include <platform_def.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +static const interrupt_prop_t fpga_interrupt_props[] = { + PLATFORM_G1S_PROPS(INTR_GROUP1S), + PLATFORM_G0_PROPS(INTR_GROUP0) +}; + +static uintptr_t fpga_rdistif_base_addrs[PLATFORM_CORE_COUNT]; +static int nr_itses; + +static unsigned int fpga_mpidr_to_core_pos(unsigned long mpidr) +{ + return (unsigned int)plat_core_pos_by_mpidr(mpidr); +} + +static gicv3_driver_data_t fpga_gicv3_driver_data = { + .interrupt_props = fpga_interrupt_props, + .interrupt_props_num = ARRAY_SIZE(fpga_interrupt_props), + .rdistif_num = PLATFORM_CORE_COUNT, + .rdistif_base_addrs = fpga_rdistif_base_addrs, + .mpidr_to_core_pos = fpga_mpidr_to_core_pos +}; + +void plat_fpga_gic_init(void) +{ + const void *fdt = (void *)(uintptr_t)FPGA_PRELOADED_DTB_BASE; + uintptr_t gicr_base = 0U; + uint32_t iidr; + int node, ret; + + node = fdt_node_offset_by_compatible(fdt, 0, "arm,gic-v3"); + if (node < 0) { + WARN("No \"arm,gic-v3\" compatible node found in DT, no GIC support.\n"); + return; + } + + /* TODO: Assuming only empty "ranges;" properties up the bus path. */ + ret = fdt_get_reg_props_by_index(fdt, node, 0, + &fpga_gicv3_driver_data.gicd_base, NULL); + if (ret < 0) { + WARN("Could not read GIC distributor address from DT.\n"); + return; + } + + iidr = mmio_read_32(fpga_gicv3_driver_data.gicd_base + GICD_IIDR); + if (((iidr & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_600) || + ((iidr & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_700)) { + unsigned int frame_id; + + /* + * According to the GIC TRMs, if there are any ITSes, they + * start four 64K pages after the distributor. After all + * the ITSes then follow the redistributors. + */ + gicr_base = fpga_gicv3_driver_data.gicd_base + (4U << 16); + + do { + uint64_t its_typer; + + /* Each GIC component can be identified by its ID. */ + frame_id = gicv3_get_component_partnum(gicr_base); + + if (frame_id == PIDR_COMPONENT_ARM_REDIST) { + INFO("Found %d ITSes, redistributors start at 0x%llx\n", + nr_itses, (unsigned long long)gicr_base); + break; + } + + if (frame_id != PIDR_COMPONENT_ARM_ITS) { + WARN("GICv3: found unexpected frame 0x%x\n", + frame_id); + gicr_base = 0U; + break; + } + + /* + * Found an ITS, now work out if it supports virtual + * SGIs (for direct guest injection). If yes, each + * ITS occupies four 64K pages, otherwise just two. + */ + its_typer = mmio_read_64(gicr_base + GITS_TYPER); + if ((its_typer & GITS_TYPER_VSGI) != 0U) { + gicr_base += 4U << 16; + } else { + gicr_base += 2U << 16; + } + nr_itses++; + } while (true); + } + + /* + * If this is not a GIC-600 or -700, or the autodetection above failed, + * use the base address from the device tree. + */ + if (gicr_base == 0U) { + ret = fdt_get_reg_props_by_index(fdt, node, 1, + &fpga_gicv3_driver_data.gicr_base, + NULL); + if (ret < 0) { + WARN("Could not read GIC redistributor address from DT.\n"); + return; + } + } else { + fpga_gicv3_driver_data.gicr_base = gicr_base; + } + + gicv3_driver_init(&fpga_gicv3_driver_data); + gicv3_distif_init(); + gicv3_rdistif_init(plat_my_core_pos()); + gicv3_cpuif_enable(plat_my_core_pos()); +} + +void fpga_pwr_gic_on_finish(void) +{ + gicv3_rdistif_init(plat_my_core_pos()); + gicv3_cpuif_enable(plat_my_core_pos()); +} + +void fpga_pwr_gic_off(void) +{ + gicv3_cpuif_disable(plat_my_core_pos()); + gicv3_rdistif_off(plat_my_core_pos()); +} + +unsigned int fpga_get_nr_gic_cores(void) +{ + return gicv3_rdistif_get_number_frames(fpga_gicv3_driver_data.gicr_base); +} + +uintptr_t fpga_get_redist_size(void) +{ + uint64_t typer_val = mmio_read_64(fpga_gicv3_driver_data.gicr_base + + GICR_TYPER); + + return gicv3_redist_size(typer_val); +} + +uintptr_t fpga_get_redist_base(void) +{ + return fpga_gicv3_driver_data.gicr_base; +} + +bool fpga_has_its(void) +{ + return nr_itses > 0; +} diff --git a/plat/arm/board/arm_fpga/fpga_pm.c b/plat/arm/board/arm_fpga/fpga_pm.c new file mode 100644 index 0000000..a306a23 --- /dev/null +++ b/plat/arm/board/arm_fpga/fpga_pm.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <lib/psci/psci.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> + +#include "fpga_private.h" +#include <platform_def.h> + +/* + * This is a basic PSCI implementation that allows secondary CPUs to be + * released from their initial state and continue to the warm boot entrypoint. + * + * The secondary CPUs are placed in a holding pen and released by calls + * to fpga_pwr_domain_on(mpidr), which updates the hold entry for the CPU + * specified by the mpidr argument - the (polling) target CPU will then branch + * to the BL31 warm boot sequence at the entrypoint address. + * + * Additionally, the secondary CPUs are kept in a low-power wfe() state + * (placed there at the end of each poll) and woken when necessary through + * calls to sev() in fpga_pwr_domain_on(mpidr), once the hold state for the + * relevant CPU has been updated. + * + * Hotplug is currently implemented using a wfi-loop, which removes the + * dependencies on any power controllers or other mechanism that is specific + * to the running system as specified by the FPGA image. + */ + +uint64_t hold_base[PLATFORM_CORE_COUNT]; +uintptr_t fpga_sec_entrypoint; + +/* + * Calls to the CPU specified by the mpidr will set its hold entry to a value + * indicating that it should stop polling and branch off to the warm entrypoint. + */ +static int fpga_pwr_domain_on(u_register_t mpidr) +{ + int pos = plat_core_pos_by_mpidr(mpidr); + unsigned long current_mpidr = read_mpidr_el1(); + + if (pos < 0) { + panic(); + } + + if (mpidr == current_mpidr) { + return PSCI_E_ALREADY_ON; + } + hold_base[pos] = PLAT_FPGA_HOLD_STATE_GO; + flush_dcache_range((uintptr_t)&hold_base[pos], sizeof(uint64_t)); + sev(); /* Wake any CPUs from wfe */ + + return PSCI_E_SUCCESS; +} + +void fpga_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + fpga_pwr_gic_on_finish(); +} + +static void fpga_pwr_domain_off(const psci_power_state_t *target_state) +{ + fpga_pwr_gic_off(); + + while (1) { + wfi(); + } +} + +static void fpga_cpu_standby(plat_local_state_t cpu_state) +{ + /* + * Enter standby state + * dsb is good practice before using wfi to enter low power states + */ + u_register_t scr = read_scr_el3(); + write_scr_el3(scr|SCR_IRQ_BIT); + dsb(); + wfi(); + write_scr_el3(scr); +} + +plat_psci_ops_t plat_fpga_psci_pm_ops = { + .pwr_domain_on = fpga_pwr_domain_on, + .pwr_domain_on_finish = fpga_pwr_domain_on_finish, + .pwr_domain_off = fpga_pwr_domain_off, + .cpu_standby = fpga_cpu_standby +}; + +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) +{ + fpga_sec_entrypoint = sec_entrypoint; + flush_dcache_range((uint64_t)&fpga_sec_entrypoint, + sizeof(fpga_sec_entrypoint)); + *psci_ops = &plat_fpga_psci_pm_ops; + return 0; +} diff --git a/plat/arm/board/arm_fpga/fpga_private.h b/plat/arm/board/arm_fpga/fpga_private.h new file mode 100644 index 0000000..84d651c --- /dev/null +++ b/plat/arm/board/arm_fpga/fpga_private.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FPGA_PRIVATE_H +#define FPGA_PRIVATE_H + +#include "../fpga_def.h" +#include <platform_def.h> + +#define C_RUNTIME_READY_KEY (0xaa55aa55) +#define VALID_MPID (1U) +#define FPGA_MAX_DTB_SIZE 0x10000 + +#ifndef __ASSEMBLER__ + +extern unsigned char fpga_valid_mpids[PLATFORM_CORE_COUNT]; + +void fpga_console_init(void); + +void plat_fpga_gic_init(void); +void fpga_pwr_gic_on_finish(void); +void fpga_pwr_gic_off(void); +unsigned int plat_fpga_calc_core_pos(uint32_t mpid); +unsigned int fpga_get_nr_gic_cores(void); +uintptr_t fpga_get_redist_size(void); +uintptr_t fpga_get_redist_base(void); +bool fpga_has_its(void); + +#endif /* __ASSEMBLER__ */ + +#endif /* FPGA_PRIVATE_H */ diff --git a/plat/arm/board/arm_fpga/fpga_topology.c b/plat/arm/board/arm_fpga/fpga_topology.c new file mode 100644 index 0000000..7fead86 --- /dev/null +++ b/plat/arm/board/arm_fpga/fpga_topology.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> +#include <common/debug.h> +#include <lib/spinlock.h> + +#include "fpga_private.h" +#include <plat/common/platform.h> +#include <platform_def.h> + +unsigned char fpga_power_domain_tree_desc[FPGA_MAX_CLUSTER_COUNT + 2]; +unsigned char fpga_valid_mpids[PLATFORM_CORE_COUNT]; + +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + unsigned int i; + + /* + * The highest level is the system level. The next level is constituted + * by clusters and then cores in clusters. + * + * This description of the power domain topology is aligned with the CPU + * indices returned by the plat_core_pos_by_mpidr() and plat_my_core_pos() + * APIs. + * + * A description of the topology tree can be found at + * https://trustedfirmware-a.readthedocs.io/en/latest/design/psci-pd-tree.html#design + */ + + if (fpga_power_domain_tree_desc[0] == 0U) { + /* + * As fpga_power_domain_tree_desc[0] == 0, assume that the + * Power Domain Topology Tree has not been initialized, so + * perform the initialization here. + */ + + fpga_power_domain_tree_desc[0] = 1U; + fpga_power_domain_tree_desc[1] = FPGA_MAX_CLUSTER_COUNT; + + for (i = 0U; i < FPGA_MAX_CLUSTER_COUNT; i++) { + fpga_power_domain_tree_desc[2 + i] = + (FPGA_MAX_CPUS_PER_CLUSTER * + FPGA_MAX_PE_PER_CPU); + } + } + + return fpga_power_domain_tree_desc; +} + +int plat_core_pos_by_mpidr(u_register_t mpidr) +{ + unsigned int core_pos; + + mpidr &= (MPID_MASK & ~(MPIDR_AFFLVL_MASK << MPIDR_AFF3_SHIFT)); + mpidr |= (read_mpidr_el1() & MPIDR_MT_MASK); + + if ((MPIDR_AFFLVL2_VAL(mpidr) >= FPGA_MAX_CLUSTER_COUNT) || + (MPIDR_AFFLVL1_VAL(mpidr) >= FPGA_MAX_CPUS_PER_CLUSTER) || + (MPIDR_AFFLVL0_VAL(mpidr) >= FPGA_MAX_PE_PER_CPU)) { + ERROR ("Invalid mpidr: 0x%08x\n", (uint32_t)mpidr); + panic(); + } + + /* Calculate the core position, based on the maximum topology. */ + core_pos = plat_fpga_calc_core_pos(mpidr); + + /* Check whether this core is actually present. */ + if (fpga_valid_mpids[core_pos] != VALID_MPID) { + return -1; + } + + return core_pos; +} diff --git a/plat/arm/board/arm_fpga/include/plat_macros.S b/plat/arm/board/arm_fpga/include/plat_macros.S new file mode 100644 index 0000000..44cddeb --- /dev/null +++ b/plat/arm/board/arm_fpga/include/plat_macros.S @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + +.macro plat_crash_print_regs +.endm + +#endif diff --git a/plat/arm/board/arm_fpga/include/platform_def.h b/plat/arm/board/arm_fpga/include/platform_def.h new file mode 100644 index 0000000..2350d87 --- /dev/null +++ b/plat/arm/board/arm_fpga/include/platform_def.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <arch.h> +#include <plat/common/common_def.h> +#include <platform_def.h> +#include "../fpga_def.h" + +#define PLATFORM_LINKER_FORMAT "elf64-littleaarch64" + +#define PLATFORM_LINKER_ARCH aarch64 + +#define PLATFORM_STACK_SIZE UL(0x800) + +#define CACHE_WRITEBACK_SHIFT U(6) +#define CACHE_WRITEBACK_GRANULE (U(1) << CACHE_WRITEBACK_SHIFT) + +#define PLATFORM_CORE_COUNT \ + (FPGA_MAX_CLUSTER_COUNT * \ + FPGA_MAX_CPUS_PER_CLUSTER * \ + FPGA_MAX_PE_PER_CPU) + +#define PLAT_NUM_PWR_DOMAINS (FPGA_MAX_CLUSTER_COUNT + PLATFORM_CORE_COUNT + 1) + +#if !ENABLE_PIE +#define BL31_BASE UL(0x80000000) +#define BL31_LIMIT UL(0x80070000) +#else +#define BL31_BASE UL(0x0) +#define BL31_LIMIT UL(0x01000000) +#endif + +#define PLAT_SDEI_NORMAL_PRI 0x70 + +#define ARM_IRQ_SEC_PHY_TIMER 29 + +#define ARM_IRQ_SEC_SGI_0 8 +#define ARM_IRQ_SEC_SGI_1 9 +#define ARM_IRQ_SEC_SGI_2 10 +#define ARM_IRQ_SEC_SGI_3 11 +#define ARM_IRQ_SEC_SGI_4 12 +#define ARM_IRQ_SEC_SGI_5 13 +#define ARM_IRQ_SEC_SGI_6 14 +#define ARM_IRQ_SEC_SGI_7 15 + +/* + * Define a list of Group 1 Secure and Group 0 interrupt properties as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define PLATFORM_G1S_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, (grp), \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_1, GIC_HIGHEST_SEC_PRIORITY, (grp), \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_2, GIC_HIGHEST_SEC_PRIORITY, (grp), \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_3, GIC_HIGHEST_SEC_PRIORITY, (grp), \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_4, GIC_HIGHEST_SEC_PRIORITY, (grp), \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_5, GIC_HIGHEST_SEC_PRIORITY, (grp), \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY, (grp), \ + GIC_INTR_CFG_EDGE) + +#define PLATFORM_G0_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, PLAT_SDEI_NORMAL_PRI, (grp), \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, (grp), \ + GIC_INTR_CFG_EDGE) + +#define PLAT_MAX_RET_STATE 1 +#define PLAT_MAX_OFF_STATE 2 + +#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL2 + +#define PLAT_FPGA_HOLD_ENTRY_SHIFT 3 +#define PLAT_FPGA_HOLD_STATE_WAIT 0 +#define PLAT_FPGA_HOLD_STATE_GO 1 + +#endif diff --git a/plat/arm/board/arm_fpga/kernel_trampoline.S b/plat/arm/board/arm_fpga/kernel_trampoline.S new file mode 100644 index 0000000..f4c08ef --- /dev/null +++ b/plat/arm/board/arm_fpga/kernel_trampoline.S @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * The traditional arm64 Linux kernel load address is 512KiB from the + * beginning of DRAM, caused by this having been the default value of the + * kernel's CONFIG_TEXT_OFFSET Kconfig value. + * However kernel version 5.8 changed the default offset (into a 2MB page) + * to 0, so TF-A's default assumption is no longer true. Fortunately the + * kernel got more relaxed about this offset at the same time, so it + * tolerates the wrong offset, but issues a warning: + * [Firmware Bug]: Kernel image misaligned at boot, please fix your bootloader! + * + * We cannot easily change the load address offset in TF-A to be 2MiB, because + * this would break older kernels - and they are not as forgiving in this + * respect. + * + * But we can allow users to load the kernel at the right offset, and + * offer this trampoline here to transition to this new load address. + * Any older kernels, or newer kernels misloaded, will overwrite this code + * here, so it does no harm in this case. + */ + +#include <asm_macros.S> +#include <common/bl_common.ld.h> + +.text +.global _tramp_start + +_tramp_start: + adr x4, _tramp_start + orr x4, x4, #0x1fffff + add x4, x4, #1 /* align up to 2MB */ + br x4 diff --git a/plat/arm/board/arm_fpga/platform.mk b/plat/arm/board/arm_fpga/platform.mk new file mode 100644 index 0000000..a14a0d8 --- /dev/null +++ b/plat/arm/board/arm_fpga/platform.mk @@ -0,0 +1,130 @@ +# +# Copyright (c) 2021-2022, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +include common/fdt_wrappers.mk +include lib/libfdt/libfdt.mk + +RESET_TO_BL31 := 1 +ifeq (${RESET_TO_BL31}, 0) +$(error "This is a BL31-only port; RESET_TO_BL31 must be enabled") +endif + +ifeq (${ENABLE_PIE}, 1) +override SEPARATE_CODE_AND_RODATA := 1 +endif + +CTX_INCLUDE_AARCH32_REGS := 0 +ifeq (${CTX_INCLUDE_AARCH32_REGS}, 1) +$(error "This is an AArch64-only port; CTX_INCLUDE_AARCH32_REGS must be disabled") +endif + +ifeq (${TRUSTED_BOARD_BOOT}, 1) +$(error "TRUSTED_BOARD_BOOT must be disabled") +endif + +PRELOADED_BL33_BASE := 0x80080000 + +FPGA_PRELOADED_DTB_BASE := 0x80070000 +$(eval $(call add_define,FPGA_PRELOADED_DTB_BASE)) + +FPGA_PRELOADED_CMD_LINE := 0x1000 +$(eval $(call add_define,FPGA_PRELOADED_CMD_LINE)) + +ENABLE_AMU := 1 + +# Treating this as a memory-constrained port for now +USE_COHERENT_MEM := 0 + +# This can be overridden depending on CPU(s) used in the FPGA image +HW_ASSISTED_COHERENCY := 1 + +PL011_GENERIC_UART := 1 + +SUPPORT_UNKNOWN_MPID ?= 1 + +FPGA_CPU_LIBS := lib/cpus/${ARCH}/aem_generic.S + +# select a different set of CPU files, depending on whether we compile for +# hardware assisted coherency cores or not +ifeq (${HW_ASSISTED_COHERENCY}, 0) +# Cores used without DSU + FPGA_CPU_LIBS += lib/cpus/aarch64/cortex_a35.S \ + lib/cpus/aarch64/cortex_a53.S \ + lib/cpus/aarch64/cortex_a57.S \ + lib/cpus/aarch64/cortex_a72.S \ + lib/cpus/aarch64/cortex_a73.S +else +# AArch64-only cores + FPGA_CPU_LIBS += lib/cpus/aarch64/cortex_a76.S \ + lib/cpus/aarch64/cortex_a76ae.S \ + lib/cpus/aarch64/cortex_a77.S \ + lib/cpus/aarch64/cortex_a78.S \ + lib/cpus/aarch64/neoverse_n_common.S \ + lib/cpus/aarch64/neoverse_n1.S \ + lib/cpus/aarch64/neoverse_n2.S \ + lib/cpus/aarch64/neoverse_e1.S \ + lib/cpus/aarch64/neoverse_v1.S \ + lib/cpus/aarch64/cortex_a78_ae.S \ + lib/cpus/aarch64/cortex_a65.S \ + lib/cpus/aarch64/cortex_a65ae.S \ + lib/cpus/aarch64/cortex_a510.S \ + lib/cpus/aarch64/cortex_a710.S \ + lib/cpus/aarch64/cortex_a715.S \ + lib/cpus/aarch64/cortex_x3.S \ + lib/cpus/aarch64/cortex_a78c.S + +# AArch64/AArch32 cores + FPGA_CPU_LIBS += lib/cpus/aarch64/cortex_a55.S \ + lib/cpus/aarch64/cortex_a75.S +endif + +ifeq (${SUPPORT_UNKNOWN_MPID}, 1) +# Add support for unknown/invalid MPIDs (aarch64 only) +$(eval $(call add_define,SUPPORT_UNKNOWN_MPID)) + FPGA_CPU_LIBS += lib/cpus/aarch64/generic.S +endif + +# Allow detection of GIC-600 +GICV3_SUPPORT_GIC600 := 1 + +GIC_ENABLE_V4_EXTN := 1 + +# Include GICv3 driver files +include drivers/arm/gic/v3/gicv3.mk + +FPGA_GIC_SOURCES := ${GICV3_SOURCES} \ + plat/common/plat_gicv3.c \ + plat/arm/board/arm_fpga/fpga_gicv3.c + +FDT_SOURCES := fdts/arm_fpga.dts + +PLAT_INCLUDES := -Iplat/arm/board/arm_fpga/include + +PLAT_BL_COMMON_SOURCES := plat/arm/board/arm_fpga/${ARCH}/fpga_helpers.S + +BL31_SOURCES += common/fdt_fixup.c \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + drivers/arm/pl011/${ARCH}/pl011_console.S \ + plat/common/plat_psci_common.c \ + plat/arm/board/arm_fpga/fpga_pm.c \ + plat/arm/board/arm_fpga/fpga_topology.c \ + plat/arm/board/arm_fpga/fpga_console.c \ + plat/arm/board/arm_fpga/fpga_bl31_setup.c \ + ${FPGA_CPU_LIBS} \ + ${FPGA_GIC_SOURCES} + +BL31_SOURCES += ${FDT_WRAPPERS_SOURCES} + +$(eval $(call MAKE_S,$(BUILD_PLAT),plat/arm/board/arm_fpga/rom_trampoline.S,bl31)) +$(eval $(call MAKE_S,$(BUILD_PLAT),plat/arm/board/arm_fpga/kernel_trampoline.S,bl31)) +$(eval $(call MAKE_LD,$(BUILD_PLAT)/build_axf.ld,plat/arm/board/arm_fpga/build_axf.ld.S,bl31)) + +bl31.axf: bl31 dtbs ${BUILD_PLAT}/rom_trampoline.o ${BUILD_PLAT}/kernel_trampoline.o ${BUILD_PLAT}/build_axf.ld + $(ECHO) " LD $@" + $(Q)$(LD) -T ${BUILD_PLAT}/build_axf.ld -L ${BUILD_PLAT} --strip-debug -s -n -o ${BUILD_PLAT}/bl31.axf + +all: bl31.axf diff --git a/plat/arm/board/arm_fpga/rom_trampoline.S b/plat/arm/board/arm_fpga/rom_trampoline.S new file mode 100644 index 0000000..cd66c79 --- /dev/null +++ b/plat/arm/board/arm_fpga/rom_trampoline.S @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * The Arm Ltd. FPGA images start execution at address 0x0, which is + * mapped at an (emulated) ROM image. The payload uploader can write to + * this memory, but write access by the CPU cores is prohibited. + * + * Provide a simple trampoline to start BL31 execution at the actual + * load address. We put the DTB address in x0, so any code in DRAM could + * make use of that information (not yet used in BL31 right now). + */ + +#include <asm_macros.S> +#include <common/bl_common.ld.h> + +.text +.global _start + +_start: + mov_imm x1, BL31_BASE /* beginning of DRAM */ + mov_imm x0, FPGA_PRELOADED_DTB_BASE + br x1 |