diff options
Diffstat (limited to 'plat/rpi/rpi4')
-rw-r--r-- | plat/rpi/rpi4/aarch64/armstub8_header.S | 37 | ||||
-rw-r--r-- | plat/rpi/rpi4/include/plat.ld.S | 23 | ||||
-rw-r--r-- | plat/rpi/rpi4/include/plat_macros.S | 20 | ||||
-rw-r--r-- | plat/rpi/rpi4/include/platform_def.h | 140 | ||||
-rw-r--r-- | plat/rpi/rpi4/include/rpi_hw.h | 114 | ||||
-rw-r--r-- | plat/rpi/rpi4/platform.mk | 116 | ||||
-rw-r--r-- | plat/rpi/rpi4/rpi4_bl31_setup.c | 304 | ||||
-rw-r--r-- | plat/rpi/rpi4/rpi4_pci_svc.c | 215 |
8 files changed, 969 insertions, 0 deletions
diff --git a/plat/rpi/rpi4/aarch64/armstub8_header.S b/plat/rpi/rpi4/aarch64/armstub8_header.S new file mode 100644 index 0000000..246358d --- /dev/null +++ b/plat/rpi/rpi4/aarch64/armstub8_header.S @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * armstub8.bin header to let the GPU firmware recognise this code. + * It will then write the load address of the kernel image and the DT + * after the header magic in RAM, so we can read those addresses at runtime. + */ + +.text + b armstub8_end + +.global stub_magic +.global dtb_ptr32 +.global kernel_entry32 + +.org 0xf0 +armstub8: +stub_magic: + .word 0x5afe570b +stub_version: + .word 0 +dtb_ptr32: + .word 0x0 +kernel_entry32: + .word 0x0 + +/* + * Technically an offset of 0x100 would suffice, but the follow-up code + * (bl31_entrypoint.S at BL31_BASE) needs to be page aligned, so pad here + * till the end of the first 4K page. + */ +.org 0x1000 +armstub8_end: diff --git a/plat/rpi/rpi4/include/plat.ld.S b/plat/rpi/rpi4/include/plat.ld.S new file mode 100644 index 0000000..9262fad --- /dev/null +++ b/plat/rpi/rpi4/include/plat.ld.S @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Stub linker script to provide the armstub8.bin header before the actual + * code. If the GPU firmware finds a magic value at offset 240 in + * armstub8.bin, it will put the DTB and kernel load address in subsequent + * words. We can then read those values to find the proper NS entry point + * and find our DTB more flexibly. + */ + +MEMORY { + PRERAM (rwx): ORIGIN = 0, LENGTH = 4096 +} + +SECTIONS +{ + .armstub8 . : { + *armstub8_header.o(.text*) + KEEP(*(.armstub8)) + } >PRERAM +} diff --git a/plat/rpi/rpi4/include/plat_macros.S b/plat/rpi/rpi4/include/plat_macros.S new file mode 100644 index 0000000..6007d03 --- /dev/null +++ b/plat/rpi/rpi4/include/plat_macros.S @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + + /* --------------------------------------------- + * The below required platform porting macro + * prints out relevant platform registers + * whenever an unhandled exception is taken in + * BL31. + * Clobbers: x0 - x10, x16, x17, sp + * --------------------------------------------- + */ + .macro plat_crash_print_regs + .endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/rpi/rpi4/include/platform_def.h b/plat/rpi/rpi4/include/platform_def.h new file mode 100644 index 0000000..6787ebf --- /dev/null +++ b/plat/rpi/rpi4/include/platform_def.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2015-2019, 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 <common/tbbr/tbbr_img_def.h> +#include <lib/utils_def.h> +#include <plat/common/common_def.h> + +#include "rpi_hw.h" + +/* Special value used to verify platform parameters from BL2 to BL31 */ +#define RPI3_BL31_PLAT_PARAM_VAL ULL(0x0F1E2D3C4B5A6978) + +#define PLATFORM_STACK_SIZE ULL(0x1000) + +#define PLATFORM_MAX_CPUS_PER_CLUSTER U(4) +#define PLATFORM_CLUSTER_COUNT U(1) +#define PLATFORM_CLUSTER0_CORE_COUNT PLATFORM_MAX_CPUS_PER_CLUSTER +#define PLATFORM_CORE_COUNT PLATFORM_CLUSTER0_CORE_COUNT + +#define RPI_PRIMARY_CPU U(0) + +#define PLAT_MAX_PWR_LVL MPIDR_AFFLVL1 +#define PLAT_NUM_PWR_DOMAINS (PLATFORM_CLUSTER_COUNT + \ + PLATFORM_CORE_COUNT) + +#define PLAT_MAX_RET_STATE U(1) +#define PLAT_MAX_OFF_STATE U(2) + +/* Local power state for power domains in Run state. */ +#define PLAT_LOCAL_STATE_RUN U(0) +/* Local power state for retention. Valid only for CPU power domains */ +#define PLAT_LOCAL_STATE_RET U(1) +/* + * Local power state for OFF/power-down. Valid for CPU and cluster power + * domains. + */ +#define PLAT_LOCAL_STATE_OFF U(2) + +/* + * Macros used to parse state information from State-ID if it is using the + * recommended encoding for State-ID. + */ +#define PLAT_LOCAL_PSTATE_WIDTH U(4) +#define PLAT_LOCAL_PSTATE_MASK ((U(1) << PLAT_LOCAL_PSTATE_WIDTH) - 1) + +/* + * Some data must be aligned on the biggest cache line size in the platform. + * This is known only to the platform as it might have a combination of + * integrated and external caches. + */ +#define CACHE_WRITEBACK_SHIFT U(6) +#define CACHE_WRITEBACK_GRANULE (U(1) << CACHE_WRITEBACK_SHIFT) + +/* + * I/O registers. + */ +#define DEVICE0_BASE RPI_IO_BASE +#define DEVICE0_SIZE RPI_IO_SIZE + +/* + * Mailbox to control the secondary cores. All secondary cores are held in a + * wait loop in cold boot. To release them perform the following steps (plus + * any additional barriers that may be needed): + * + * uint64_t *entrypoint = (uint64_t *)PLAT_RPI3_TM_ENTRYPOINT; + * *entrypoint = ADDRESS_TO_JUMP_TO; + * + * uint64_t *mbox_entry = (uint64_t *)PLAT_RPI3_TM_HOLD_BASE; + * mbox_entry[cpu_id] = PLAT_RPI3_TM_HOLD_STATE_GO; + * + * sev(); + */ +/* The secure entry point to be used on warm reset by all CPUs. */ +#define PLAT_RPI3_TM_ENTRYPOINT 0x100 +#define PLAT_RPI3_TM_ENTRYPOINT_SIZE ULL(8) + +/* Hold entries for each CPU. */ +#define PLAT_RPI3_TM_HOLD_BASE (PLAT_RPI3_TM_ENTRYPOINT + \ + PLAT_RPI3_TM_ENTRYPOINT_SIZE) +#define PLAT_RPI3_TM_HOLD_ENTRY_SIZE ULL(8) +#define PLAT_RPI3_TM_HOLD_SIZE (PLAT_RPI3_TM_HOLD_ENTRY_SIZE * \ + PLATFORM_CORE_COUNT) + +#define PLAT_RPI3_TRUSTED_MAILBOX_SIZE (PLAT_RPI3_TM_ENTRYPOINT_SIZE + \ + PLAT_RPI3_TM_HOLD_SIZE) + +#define PLAT_RPI3_TM_HOLD_STATE_WAIT ULL(0) +#define PLAT_RPI3_TM_HOLD_STATE_GO ULL(1) +#define PLAT_RPI3_TM_HOLD_STATE_BSP_OFF ULL(2) + +/* + * BL31 specific defines. + * + * Put BL31 at the top of the Trusted SRAM. BL31_BASE is calculated using the + * current BL31 debug size plus a little space for growth. + */ +#define PLAT_MAX_BL31_SIZE ULL(0x80000) + +#define BL31_BASE ULL(0x1000) +#define BL31_LIMIT ULL(0x80000) +#define BL31_PROGBITS_LIMIT ULL(0x80000) + +#define SEC_SRAM_ID 0 +#define SEC_DRAM_ID 1 + +/* + * Other memory-related defines. + */ +#define PLAT_PHY_ADDR_SPACE_SIZE (ULL(1) << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (ULL(1) << 32) + +#define MAX_MMAP_REGIONS 8 +#define MAX_XLAT_TABLES 4 + +#define MAX_IO_DEVICES U(3) +#define MAX_IO_HANDLES U(4) + +#define MAX_IO_BLOCK_DEVICES U(1) + +/* + * Serial-related constants. + */ +#define PLAT_RPI_MINI_UART_BASE RPI4_MINI_UART_BASE +#define PLAT_RPI_PL011_UART_BASE RPI4_PL011_UART_BASE +#define PLAT_RPI_PL011_UART_CLOCK RPI4_PL011_UART_CLOCK +#define PLAT_RPI_UART_BAUDRATE ULL(115200) + +/* + * System counter + */ +#define SYS_COUNTER_FREQ_IN_TICKS ULL(54000000) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/rpi/rpi4/include/rpi_hw.h b/plat/rpi/rpi4/include/rpi_hw.h new file mode 100644 index 0000000..0430d46 --- /dev/null +++ b/plat/rpi/rpi4/include/rpi_hw.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RPI_HW_H +#define RPI_HW_H + +#include <lib/utils_def.h> + +/* + * Peripherals + */ + +#define RPI_IO_BASE ULL(0xFC000000) +#define RPI_IO_SIZE ULL(0x04000000) + +#define RPI_LEGACY_BASE (ULL(0x02000000) + RPI_IO_BASE) + +/* + * ARM <-> VideoCore mailboxes + */ +#define RPI3_MBOX_OFFSET ULL(0x0000B880) +#define RPI3_MBOX_BASE (RPI_LEGACY_BASE + RPI3_MBOX_OFFSET) +/* VideoCore -> ARM */ +#define RPI3_MBOX0_READ_OFFSET ULL(0x00000000) +#define RPI3_MBOX0_PEEK_OFFSET ULL(0x00000010) +#define RPI3_MBOX0_SENDER_OFFSET ULL(0x00000014) +#define RPI3_MBOX0_STATUS_OFFSET ULL(0x00000018) +#define RPI3_MBOX0_CONFIG_OFFSET ULL(0x0000001C) +/* ARM -> VideoCore */ +#define RPI3_MBOX1_WRITE_OFFSET ULL(0x00000020) +#define RPI3_MBOX1_PEEK_OFFSET ULL(0x00000030) +#define RPI3_MBOX1_SENDER_OFFSET ULL(0x00000034) +#define RPI3_MBOX1_STATUS_OFFSET ULL(0x00000038) +#define RPI3_MBOX1_CONFIG_OFFSET ULL(0x0000003C) +/* Mailbox status constants */ +#define RPI3_MBOX_STATUS_FULL_MASK U(0x80000000) /* Set if full */ +#define RPI3_MBOX_STATUS_EMPTY_MASK U(0x40000000) /* Set if empty */ + +/* + * Power management, reset controller, watchdog. + */ +#define RPI3_IO_PM_OFFSET ULL(0x00100000) +#define RPI3_PM_BASE (RPI_LEGACY_BASE + RPI3_IO_PM_OFFSET) +/* Registers on top of RPI3_PM_BASE. */ +#define RPI3_PM_RSTC_OFFSET ULL(0x0000001C) +#define RPI3_PM_RSTS_OFFSET ULL(0x00000020) +#define RPI3_PM_WDOG_OFFSET ULL(0x00000024) +/* Watchdog constants */ +#define RPI3_PM_PASSWORD U(0x5A000000) +#define RPI3_PM_RSTC_WRCFG_MASK U(0x00000030) +#define RPI3_PM_RSTC_WRCFG_FULL_RESET U(0x00000020) +/* + * The RSTS register is used by the VideoCore firmware when booting the + * Raspberry Pi to know which partition to boot from. The partition value is + * formed by bits 0, 2, 4, 6, 8 and 10. Partition 63 is used by said firmware + * to indicate halt. + */ +#define RPI3_PM_RSTS_WRCFG_HALT U(0x00000555) + +/* + * Hardware random number generator. + */ +#define RPI3_IO_RNG_OFFSET ULL(0x00104000) +#define RPI3_RNG_BASE (RPI_LEGACY_BASE + RPI3_IO_RNG_OFFSET) +#define RPI3_RNG_CTRL_OFFSET ULL(0x00000000) +#define RPI3_RNG_STATUS_OFFSET ULL(0x00000004) +#define RPI3_RNG_DATA_OFFSET ULL(0x00000008) +#define RPI3_RNG_INT_MASK_OFFSET ULL(0x00000010) +/* Enable/disable RNG */ +#define RPI3_RNG_CTRL_ENABLE U(0x1) +#define RPI3_RNG_CTRL_DISABLE U(0x0) +/* Number of currently available words */ +#define RPI3_RNG_STATUS_NUM_WORDS_SHIFT U(24) +#define RPI3_RNG_STATUS_NUM_WORDS_MASK U(0xFF) +/* Value to mask interrupts caused by the RNG */ +#define RPI3_RNG_INT_MASK_DISABLE U(0x1) + +/* + * Serial ports: + * 'Mini UART' in the BCM docucmentation is the 8250 compatible UART. + * There is also a PL011 UART, multiplexed to the same pins. + */ +#define RPI4_IO_MINI_UART_OFFSET ULL(0x00215040) +#define RPI4_MINI_UART_BASE (RPI_LEGACY_BASE + RPI4_IO_MINI_UART_OFFSET) +#define RPI4_IO_PL011_UART_OFFSET ULL(0x00201000) +#define RPI4_PL011_UART_BASE (RPI_LEGACY_BASE + RPI4_IO_PL011_UART_OFFSET) +#define RPI4_PL011_UART_CLOCK ULL(48000000) + +/* + * GPIO controller + */ +#define RPI3_IO_GPIO_OFFSET ULL(0x00200000) +#define RPI3_GPIO_BASE (RPI_LEGACY_BASE + RPI3_IO_GPIO_OFFSET) + +/* + * SDHost controller + */ +#define RPI3_IO_SDHOST_OFFSET ULL(0x00202000) +#define RPI3_SDHOST_BASE (RPI_LEGACY_BASE + RPI3_IO_SDHOST_OFFSET) + +/* + * GIC interrupt controller + */ +#define RPI_HAVE_GIC +#define RPI4_GIC_GICD_BASE ULL(0xff841000) +#define RPI4_GIC_GICC_BASE ULL(0xff842000) + +#define RPI4_LOCAL_CONTROL_BASE_ADDRESS ULL(0xff800000) +#define RPI4_LOCAL_CONTROL_PRESCALER ULL(0xff800008) + +#endif /* RPI_HW_H */ diff --git a/plat/rpi/rpi4/platform.mk b/plat/rpi/rpi4/platform.mk new file mode 100644 index 0000000..528eb1d --- /dev/null +++ b/plat/rpi/rpi4/platform.mk @@ -0,0 +1,116 @@ +# +# Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +include lib/libfdt/libfdt.mk +include lib/xlat_tables_v2/xlat_tables.mk + +include drivers/arm/gic/v2/gicv2.mk + +PLAT_INCLUDES := -Iplat/rpi/common/include \ + -Iplat/rpi/rpi4/include + +PLAT_BL_COMMON_SOURCES := drivers/ti/uart/aarch64/16550_console.S \ + drivers/arm/pl011/aarch64/pl011_console.S \ + plat/rpi/common/rpi3_common.c \ + ${XLAT_TABLES_LIB_SRCS} + +BL31_SOURCES += lib/cpus/aarch64/cortex_a72.S \ + plat/rpi/common/aarch64/plat_helpers.S \ + plat/rpi/rpi4/aarch64/armstub8_header.S \ + drivers/delay_timer/delay_timer.c \ + drivers/gpio/gpio.c \ + drivers/rpi3/gpio/rpi3_gpio.c \ + plat/common/plat_gicv2.c \ + plat/rpi/rpi4/rpi4_bl31_setup.c \ + plat/rpi/common/rpi3_pm.c \ + plat/common/plat_psci_common.c \ + plat/rpi/common/rpi3_topology.c \ + common/fdt_fixup.c \ + ${LIBFDT_SRCS} \ + ${GICV2_SOURCES} + +# For now we only support BL31, using the kernel loaded by the GPU firmware. +RESET_TO_BL31 := 1 + +# All CPUs enter armstub8.bin. +COLD_BOOT_SINGLE_CPU := 0 + +# Tune compiler for Cortex-A72 +ifeq ($(notdir $(CC)),armclang) + TF_CFLAGS_aarch64 += -mcpu=cortex-a72 +else ifneq ($(findstring clang,$(notdir $(CC))),) + TF_CFLAGS_aarch64 += -mcpu=cortex-a72 +else + TF_CFLAGS_aarch64 += -mtune=cortex-a72 +endif + +# Add support for platform supplied linker script for BL31 build +$(eval $(call add_define,PLAT_EXTRA_LD_SCRIPT)) + +# Enable all errata workarounds for Cortex-A72 +ERRATA_A72_859971 := 1 + +WORKAROUND_CVE_2017_5715 := 1 + +# Add new default target when compiling this platform +all: bl31 + +# Build config flags +# ------------------ + +# Disable stack protector by default +ENABLE_STACK_PROTECTOR := 0 + +# Have different sections for code and rodata +SEPARATE_CODE_AND_RODATA := 1 + +# Use Coherent memory +USE_COHERENT_MEM := 1 + +# Platform build flags +# -------------------- + +# There is not much else than a Linux kernel to load at the moment. +RPI3_DIRECT_LINUX_BOOT := 1 + +# BL33 images are in AArch64 by default +RPI3_BL33_IN_AARCH32 := 0 + +# UART to use at runtime. -1 means the runtime UART is disabled. +# Any other value means the default UART will be used. +RPI3_RUNTIME_UART := 0 + +# Use normal memory mapping for ROM, FIP, SRAM and DRAM +RPI3_USE_UEFI_MAP := 0 + +# SMCCC PCI support (should be enabled for ACPI builds) +SMC_PCI_SUPPORT := 0 + +# Process platform flags +# ---------------------- + +$(eval $(call add_define,RPI3_BL33_IN_AARCH32)) +$(eval $(call add_define,RPI3_DIRECT_LINUX_BOOT)) +ifdef RPI3_PRELOADED_DTB_BASE +$(eval $(call add_define,RPI3_PRELOADED_DTB_BASE)) +endif +$(eval $(call add_define,RPI3_RUNTIME_UART)) +$(eval $(call add_define,RPI3_USE_UEFI_MAP)) +$(eval $(call add_define,SMC_PCI_SUPPORT)) + +ifeq (${ARCH},aarch32) + $(error Error: AArch32 not supported on rpi4) +endif + +ifneq ($(ENABLE_STACK_PROTECTOR), 0) +PLAT_BL_COMMON_SOURCES += drivers/rpi3/rng/rpi3_rng.c \ + plat/rpi/common/rpi3_stack_protector.c +endif + +ifeq ($(SMC_PCI_SUPPORT), 1) +BL31_SOURCES += plat/rpi/rpi4/rpi4_pci_svc.c +endif + diff --git a/plat/rpi/rpi4/rpi4_bl31_setup.c b/plat/rpi/rpi4/rpi4_bl31_setup.c new file mode 100644 index 0000000..2fb4d3d --- /dev/null +++ b/plat/rpi/rpi4/rpi4_bl31_setup.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <inttypes.h> +#include <stdint.h> + +#include <libfdt.h> + +#include <platform_def.h> +#include <arch_helpers.h> +#include <common/bl_common.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_mmu_helpers.h> +#include <lib/xlat_tables/xlat_tables_defs.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> +#include <common/fdt_fixup.h> +#include <common/fdt_wrappers.h> +#include <libfdt.h> + +#include <drivers/arm/gicv2.h> + +#include <rpi_shared.h> + +/* + * Fields at the beginning of armstub8.bin. + * While building the BL31 image, we put the stub magic into the binary. + * The GPU firmware detects this at boot time, clears that field as a + * confirmation and puts the kernel and DT address in the following words. + */ +extern uint32_t stub_magic; +extern uint32_t dtb_ptr32; +extern uint32_t kernel_entry32; + +static const gicv2_driver_data_t rpi4_gic_data = { + .gicd_base = RPI4_GIC_GICD_BASE, + .gicc_base = RPI4_GIC_GICC_BASE, +}; + +/* + * To be filled by the code below. At the moment BL32 is not supported. + * In the future these might be passed down from BL2. + */ +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +/******************************************************************************* + * Return a pointer to the 'entry_point_info' structure of the next image for + * the security state specified. BL33 corresponds to the non-secure image type + * while BL32 corresponds to the secure image type. A NULL pointer is returned + * if the image does not exist. + ******************************************************************************/ +entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) +{ + entry_point_info_t *next_image_info; + + assert(sec_state_is_valid(type) != 0); + + next_image_info = (type == NON_SECURE) + ? &bl33_image_ep_info : &bl32_image_ep_info; + + /* None of the images can have 0x0 as the entrypoint. */ + if (next_image_info->pc) { + return next_image_info; + } else { + return NULL; + } +} + +uintptr_t plat_get_ns_image_entrypoint(void) +{ +#ifdef PRELOADED_BL33_BASE + return PRELOADED_BL33_BASE; +#else + /* Cleared by the GPU if kernel address is valid. */ + if (stub_magic == 0) + return kernel_entry32; + + WARN("Stub magic failure, using default kernel address 0x80000\n"); + return 0x80000; +#endif +} + +static uintptr_t rpi4_get_dtb_address(void) +{ +#ifdef RPI3_PRELOADED_DTB_BASE + return RPI3_PRELOADED_DTB_BASE; +#else + /* Cleared by the GPU if DTB address is valid. */ + if (stub_magic == 0) + return dtb_ptr32; + + WARN("Stub magic failure, DTB address unknown\n"); + return 0; +#endif +} + +static void ldelay(register_t delay) +{ + __asm__ volatile ( + "1:\tcbz %0, 2f\n\t" + "sub %0, %0, #1\n\t" + "b 1b\n" + "2:" + : "=&r" (delay) : "0" (delay) + ); +} + +/******************************************************************************* + * Perform any BL31 early platform setup. Here is an opportunity to copy + * parameters passed by the calling EL (S-EL1 in BL2 & EL3 in BL1) before + * they are lost (potentially). This needs to be done before the MMU is + * initialized so that the memory layout can be used while creating page + * tables. BL2 has flushed this information to memory, so we are guaranteed + * to pick up good data. + ******************************************************************************/ +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) + +{ + /* + * LOCAL_CONTROL: + * Bit 9 clear: Increment by 1 (vs. 2). + * Bit 8 clear: Timer source is 19.2MHz crystal (vs. APB). + */ + mmio_write_32(RPI4_LOCAL_CONTROL_BASE_ADDRESS, 0); + + /* LOCAL_PRESCALER; divide-by (0x80000000 / register_val) == 1 */ + mmio_write_32(RPI4_LOCAL_CONTROL_PRESCALER, 0x80000000); + + /* Early GPU firmware revisions need a little break here. */ + ldelay(100000); + + /* Initialize the console to provide early debug support. */ + rpi3_console_init(); + + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); + bl33_image_ep_info.spsr = rpi3_get_spsr_for_bl33_entry(); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); + +#if RPI3_DIRECT_LINUX_BOOT +# if RPI3_BL33_IN_AARCH32 + /* + * According to the file ``Documentation/arm/Booting`` of the Linux + * kernel tree, Linux expects: + * r0 = 0 + * r1 = machine type number, optional in DT-only platforms (~0 if so) + * r2 = Physical address of the device tree blob + */ + VERBOSE("rpi4: Preparing to boot 32-bit Linux kernel\n"); + bl33_image_ep_info.args.arg0 = 0U; + bl33_image_ep_info.args.arg1 = ~0U; + bl33_image_ep_info.args.arg2 = rpi4_get_dtb_address(); +# else + /* + * According to the file ``Documentation/arm64/booting.txt`` of the + * Linux kernel tree, Linux expects the physical address of the device + * tree blob (DTB) in x0, while x1-x3 are reserved for future use and + * must be 0. + */ + VERBOSE("rpi4: Preparing to boot 64-bit Linux kernel\n"); + bl33_image_ep_info.args.arg0 = rpi4_get_dtb_address(); + bl33_image_ep_info.args.arg1 = 0ULL; + bl33_image_ep_info.args.arg2 = 0ULL; + bl33_image_ep_info.args.arg3 = 0ULL; +# endif /* RPI3_BL33_IN_AARCH32 */ +#endif /* RPI3_DIRECT_LINUX_BOOT */ +} + +void bl31_plat_arch_setup(void) +{ + /* + * Is the dtb_ptr32 pointer valid? If yes, map the DTB region. + * We map the 2MB region the DTB start address lives in, plus + * the next 2MB, to have enough room for expansion. + */ + if (stub_magic == 0) { + unsigned long long dtb_region = dtb_ptr32; + + dtb_region &= ~0x1fffff; /* Align to 2 MB. */ + mmap_add_region(dtb_region, dtb_region, 4U << 20, + MT_MEMORY | MT_RW | MT_NS); + } + /* + * Add the first page of memory, which holds the stub magic, + * the kernel and the DT address. + * This also holds the secondary CPU's entrypoints and mailboxes. + */ + mmap_add_region(0, 0, 4096, MT_NON_CACHEABLE | MT_RW | MT_SECURE); + + rpi3_setup_page_tables(BL31_BASE, BL31_END - BL31_BASE, + BL_CODE_BASE, BL_CODE_END, + BL_RO_DATA_BASE, BL_RO_DATA_END +#if USE_COHERENT_MEM + , BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END +#endif + ); + + enable_mmu_el3(0); +} + +/* + * Remove the FDT /memreserve/ entry that covers the region at the very + * beginning of memory (if that exists). This is where the secondaries + * originally spin, but we pull them out there. + * Having overlapping /reserved-memory and /memreserve/ regions confuses + * the Linux kernel, so we need to get rid of this one. + */ +static void remove_spintable_memreserve(void *dtb) +{ + uint64_t addr, size; + int regions = fdt_num_mem_rsv(dtb); + int i; + + for (i = 0; i < regions; i++) { + if (fdt_get_mem_rsv(dtb, i, &addr, &size) != 0) { + return; + } + if (size == 0U) { + return; + } + /* We only look for the region at the beginning of DRAM. */ + if (addr != 0U) { + continue; + } + /* + * Currently the region in the existing DTs is exactly 4K + * in size. Should this value ever change, there is probably + * a reason for that, so inform the user about this. + */ + if (size == 4096U) { + fdt_del_mem_rsv(dtb, i); + return; + } + WARN("Keeping unknown /memreserve/ region at 0, size: %" PRId64 "\n", + size); + } +} + +static void rpi4_prepare_dtb(void) +{ + void *dtb = (void *)rpi4_get_dtb_address(); + uint32_t gic_int_prop[3]; + int ret, offs; + + /* Return if no device tree is detected */ + if (fdt_check_header(dtb) != 0) + return; + + ret = fdt_open_into(dtb, dtb, 0x100000); + if (ret < 0) { + ERROR("Invalid Device Tree at %p: error %d\n", dtb, ret); + return; + } + + if (dt_add_psci_node(dtb)) { + ERROR("Failed to add PSCI Device Tree node\n"); + return; + } + + if (dt_add_psci_cpu_enable_methods(dtb)) { + ERROR("Failed to add PSCI cpu enable methods in Device Tree\n"); + return; + } + + /* + * Remove the original reserved region (used for the spintable), and + * replace it with a region describing the whole of Trusted Firmware. + */ + remove_spintable_memreserve(dtb); + if (fdt_add_reserved_memory(dtb, "atf@0", 0, 0x80000)) + WARN("Failed to add reserved memory nodes to DT.\n"); + + offs = fdt_node_offset_by_compatible(dtb, 0, "arm,gic-400"); + gic_int_prop[0] = cpu_to_fdt32(1); // PPI + gic_int_prop[1] = cpu_to_fdt32(9); // PPI #9 + gic_int_prop[2] = cpu_to_fdt32(0x0f04); // all cores, level high + fdt_setprop(dtb, offs, "interrupts", gic_int_prop, 12); + + offs = fdt_path_offset(dtb, "/chosen"); + fdt_setprop_string(dtb, offs, "stdout-path", "serial0"); + + ret = fdt_pack(dtb); + if (ret < 0) + ERROR("Failed to pack Device Tree at %p: error %d\n", dtb, ret); + + clean_dcache_range((uintptr_t)dtb, fdt_blob_size(dtb)); + INFO("Changed device tree to advertise PSCI.\n"); +} + +void bl31_platform_setup(void) +{ + rpi4_prepare_dtb(); + + /* Configure the interrupt controller */ + gicv2_driver_init(&rpi4_gic_data); + gicv2_distif_init(); + gicv2_pcpu_distif_init(); + gicv2_cpuif_enable(); +} diff --git a/plat/rpi/rpi4/rpi4_pci_svc.c b/plat/rpi/rpi4/rpi4_pci_svc.c new file mode 100644 index 0000000..7d1ca5c --- /dev/null +++ b/plat/rpi/rpi4/rpi4_pci_svc.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * The RPi4 has a single nonstandard PCI config region. It is broken into two + * pieces, the root port config registers and a window to a single device's + * config space which can move between devices. There isn't (yet) an + * authoritative public document on this since the available BCM2711 reference + * notes that there is a PCIe root port in the memory map but doesn't describe + * it. Given that it's not ECAM compliant yet reasonably simple, it makes for + * an excellent example of the PCI SMCCC interface. + * + * The PCI SMCCC interface is described in DEN0115 availabe from: + * https://developer.arm.com/documentation/den0115/latest + */ + +#include <assert.h> +#include <stdint.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/pmf/pmf.h> +#include <lib/runtime_instr.h> +#include <services/pci_svc.h> +#include <services/sdei.h> +#include <services/std_svc.h> +#include <smccc_helpers.h> + +#include <lib/mmio.h> + +static spinlock_t pci_lock; + +#define PCIE_REG_BASE U(RPI_IO_BASE + 0x01500000) +#define PCIE_MISC_PCIE_STATUS 0x4068 +#define PCIE_EXT_CFG_INDEX 0x9000 +/* A small window pointing at the ECAM of the device selected by CFG_INDEX */ +#define PCIE_EXT_CFG_DATA 0x8000 +#define INVALID_PCI_ADDR 0xFFFFFFFF + +#define PCIE_EXT_BUS_SHIFT 20 +#define PCIE_EXT_DEV_SHIFT 15 +#define PCIE_EXT_FUN_SHIFT 12 + + +static uint64_t pci_segment_lib_get_base(uint32_t address, uint32_t offset) +{ + uint64_t base; + uint32_t bus, dev, fun; + uint32_t status; + + base = PCIE_REG_BASE; + + offset &= PCI_OFFSET_MASK; /* Pick off the 4k register offset */ + + /* The root port is at the base of the PCIe register space */ + if (address != 0U) { + /* + * The current device must be at CFG_DATA, a 4K window mapped, + * via CFG_INDEX, to the device we are accessing. At the same + * time we must avoid accesses to certain areas of the cfg + * space via CFG_DATA. Detect those accesses and report that + * the address is invalid. + */ + base += PCIE_EXT_CFG_DATA; + bus = PCI_ADDR_BUS(address); + dev = PCI_ADDR_DEV(address); + fun = PCI_ADDR_FUN(address); + address = (bus << PCIE_EXT_BUS_SHIFT) | + (dev << PCIE_EXT_DEV_SHIFT) | + (fun << PCIE_EXT_FUN_SHIFT); + + /* Allow only dev = 0 on root port and bus 1 */ + if ((bus < 2U) && (dev > 0U)) { + return INVALID_PCI_ADDR; + } + + /* Assure link up before reading bus 1 */ + status = mmio_read_32(PCIE_REG_BASE + PCIE_MISC_PCIE_STATUS); + if ((status & 0x30) != 0x30) { + return INVALID_PCI_ADDR; + } + + /* Adjust which device the CFG_DATA window is pointing at */ + mmio_write_32(PCIE_REG_BASE + PCIE_EXT_CFG_INDEX, address); + } + return base + offset; +} + +/** + * pci_read_config() - Performs a config space read at addr + * @addr: 32-bit, segment, BDF of requested function encoded per DEN0115 + * @off: register offset of function described by @addr to read + * @sz: size of read (8,16,32) bits. + * @val: returned zero extended value read from config space + * + * sz bits of PCI config space is read at addr:offset, and the value + * is returned in val. Invalid segment/offset values return failure. + * Reads to valid functions that don't exist return INVALID_PCI_ADDR + * as is specified by PCI for requests that aren't completed by EPs. + * The boilerplate in pci_svc.c tends to do basic segment, off + * and sz validation. This routine should avoid duplicating those + * checks. + * + * This function maps directly to the PCI_READ function in DEN0115 + * where detailed requirements may be found. + * + * Return: SMC_PCI_CALL_SUCCESS with val set + * SMC_PCI_CALL_INVAL_PARAM, on parameter error + */ +uint32_t pci_read_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t *val) +{ + uint32_t ret = SMC_PCI_CALL_SUCCESS; + uint64_t base; + + spin_lock(&pci_lock); + base = pci_segment_lib_get_base(addr, off); + + if (base == INVALID_PCI_ADDR) { + *val = base; + } else { + switch (sz) { + case SMC_PCI_SZ_8BIT: + *val = mmio_read_8(base); + break; + case SMC_PCI_SZ_16BIT: + *val = mmio_read_16(base); + break; + case SMC_PCI_SZ_32BIT: + *val = mmio_read_32(base); + break; + default: /* should be unreachable */ + *val = 0; + ret = SMC_PCI_CALL_INVAL_PARAM; + } + } + spin_unlock(&pci_lock); + return ret; +} + +/** + * pci_write_config() - Performs a config space write at addr + * @addr: 32-bit, segment, BDF of requested function encoded per DEN0115 + * @off: register offset of function described by @addr to write + * @sz: size of write (8,16,32) bits. + * @val: value to be written + * + * sz bits of PCI config space is written at addr:offset. Invalid + * segment/BDF values return failure. Writes to valid functions + * without valid EPs are ignored, as is specified by PCI. + * The boilerplate in pci_svc.c tends to do basic segment, off + * and sz validation, so it shouldn't need to be repeated here. + * + * This function maps directly to the PCI_WRITE function in DEN0115 + * where detailed requirements may be found. + * + * Return: SMC_PCI_CALL_SUCCESS + * SMC_PCI_CALL_INVAL_PARAM, on parameter error + */ +uint32_t pci_write_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t val) +{ + uint32_t ret = SMC_PCI_CALL_SUCCESS; + uint64_t base; + + spin_lock(&pci_lock); + base = pci_segment_lib_get_base(addr, off); + + if (base != INVALID_PCI_ADDR) { + switch (sz) { + case SMC_PCI_SZ_8BIT: + mmio_write_8(base, val); + break; + case SMC_PCI_SZ_16BIT: + mmio_write_16(base, val); + break; + case SMC_PCI_SZ_32BIT: + mmio_write_32(base, val); + break; + default: /* should be unreachable */ + ret = SMC_PCI_CALL_INVAL_PARAM; + } + } + spin_unlock(&pci_lock); + return ret; +} + +/** + * pci_get_bus_for_seg() - returns the start->end bus range for a segment + * @seg: segment being queried + * @bus_range: returned bus begin + (end << 8) + * @nseg: returns next segment in this machine or 0 for end + * + * pci_get_bus_for_seg is called to check if a given segment is + * valid on this machine. If it is valid, then its bus ranges are + * returned along with the next valid segment on the machine. If + * this is the last segment, then nseg must be 0. + * + * This function maps directly to the PCI_GET_SEG_INFO function + * in DEN0115 where detailed requirements may be found. + * + * Return: SMC_PCI_CALL_SUCCESS, and appropriate bus_range and nseg + * SMC_PCI_CALL_NOT_IMPL, if the segment is invalid + */ +uint32_t pci_get_bus_for_seg(uint32_t seg, uint32_t *bus_range, uint32_t *nseg) +{ + uint32_t ret = SMC_PCI_CALL_SUCCESS; + *nseg = 0U; /* only a single segment */ + if (seg == 0U) { + *bus_range = 0xFF00; /* start 0, end 255 */ + } else { + *bus_range = 0U; + ret = SMC_PCI_CALL_NOT_IMPL; + } + return ret; +} |