GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -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:
@@ -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
}
@@ -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 */
@@ -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 */
@@ -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 */
@@ -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
@@ -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();
}
@@ -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;
}