diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h index 9c148ad..47e1ffc 100644 --- a/plat/st/stm32mp1/include/platform_def.h +++ b/plat/st/stm32mp1/include/platform_def.h @@ -18,7 +18,11 @@ ******************************************************************************/ /* Size of cacheable stacks */ +#if defined(IMAGE_BL32) +#define PLATFORM_STACK_SIZE 0x600 +#else #define PLATFORM_STACK_SIZE 0xC00 +#endif /* SSBL = second stage boot loader */ #define BL33_IMAGE_NAME "ssbl" diff --git a/plat/st/stm32mp1/include/stm32mp1_private.h b/plat/st/stm32mp1/include/stm32mp1_private.h index 41c46e8..a789d53 100644 --- a/plat/st/stm32mp1/include/stm32mp1_private.h +++ b/plat/st/stm32mp1/include/stm32mp1_private.h @@ -11,8 +11,12 @@ void configure_mmu(void); void stm32mp1_arch_security_setup(void); +void stm32mp1_security_setup(void); void stm32mp1_save_boot_ctx_address(uintptr_t address); uintptr_t stm32mp1_get_boot_ctx_address(void); +void stm32mp1_gic_pcpu_init(void); +void stm32mp1_gic_init(void); + #endif /* __STM32MP1_PRIVATE_H__ */ diff --git a/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk new file mode 100644 index 0000000..9fde153 --- /dev/null +++ b/plat/st/stm32mp1/sp_min/sp_min-stm32mp1.mk @@ -0,0 +1,21 @@ +# +# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +SP_MIN_WITH_SECURE_FIQ := 1 + +BL32_SOURCES += plat/common/aarch32/platform_mp_stack.S \ + plat/st/stm32mp1/sp_min/sp_min_setup.c \ + plat/st/stm32mp1/stm32mp1_pm.c \ + plat/st/stm32mp1/stm32mp1_topology.c +# Generic GIC v2 +BL32_SOURCES += drivers/arm/gic/common/gic_common.c \ + drivers/arm/gic/v2/gicv2_helpers.c \ + drivers/arm/gic/v2/gicv2_main.c \ + plat/common/plat_gicv2.c \ + plat/st/stm32mp1/stm32mp1_gic.c + +# Generic PSCI +BL32_SOURCES += plat/common/plat_psci_common.c diff --git a/plat/st/stm32mp1/sp_min/sp_min_setup.c b/plat/st/stm32mp1/sp_min/sp_min_setup.c new file mode 100644 index 0000000..1329bdb --- /dev/null +++ b/plat/st/stm32mp1/sp_min/sp_min_setup.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************** + * Placeholder variables for copying the arguments that have been passed to + * BL32 from BL2. + ******************************************************************************/ +static entry_point_info_t bl33_image_ep_info; + +/******************************************************************************* + * Interrupt handler for FIQ (secure IRQ) + ******************************************************************************/ +void sp_min_plat_fiq_handler(uint32_t id) +{ + switch (id) { + case STM32MP1_IRQ_TZC400: + ERROR("STM32MP1_IRQ_TZC400 generated\n"); + panic(); + break; + case STM32MP1_IRQ_AXIERRIRQ: + ERROR("STM32MP1_IRQ_AXIERRIRQ generated\n"); + panic(); + break; + default: + ERROR("SECURE IT handler not define for it : %i", id); + break; + } +} + +/******************************************************************************* + * 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 *sp_min_plat_get_bl33_ep_info(void) +{ + entry_point_info_t *next_image_info; + + next_image_info = &bl33_image_ep_info; + + if (next_image_info->pc == 0U) { + return NULL; + } + + return next_image_info; +} + +/******************************************************************************* + * Perform any BL32 specific platform actions. + ******************************************************************************/ +void sp_min_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + struct dt_node_info dt_dev_info; + int result; + bl_params_t *params_from_bl2 = (bl_params_t *)arg0; + + /* Imprecise aborts can be masked in NonSecure */ + write_scr(read_scr() | SCR_AW_BIT); + + assert(params_from_bl2 != NULL); + assert(params_from_bl2->h.type == PARAM_BL_PARAMS); + assert(params_from_bl2->h.version >= VERSION_2); + + bl_params_node_t *bl_params = params_from_bl2->head; + + /* + * Copy BL33 entry point information. + * They are stored in Secure RAM, in BL2's address space. + */ + while (bl_params != NULL) { + if (bl_params->image_id == BL33_IMAGE_ID) { + bl33_image_ep_info = *bl_params->ep_info; + break; + } + + bl_params = bl_params->next_params_info; + } + + if (dt_open_and_check() < 0) { + panic(); + } + + if (stm32mp1_clk_probe() < 0) { + panic(); + } + + result = dt_get_stdout_uart_info(&dt_dev_info); + + if ((result > 0) && dt_dev_info.status) { + if (console_init(dt_dev_info.base, 0, STM32MP1_UART_BAUDRATE) + == 0) { + panic(); + } + } +} + +/******************************************************************************* + * Initialize the MMU, security and the GIC. + ******************************************************************************/ +void sp_min_platform_setup(void) +{ + mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, + BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE); + + configure_mmu(); + + /* Initialize tzc400 after DDR initialization */ + stm32mp1_security_setup(); + + generic_delay_timer_init(); + + stm32mp1_gic_init(); +} + +void sp_min_plat_arch_setup(void) +{ +} diff --git a/plat/st/stm32mp1/stm32mp1_common.c b/plat/st/stm32mp1/stm32mp1_common.c index e2f90d2..68ca7db 100644 --- a/plat/st/stm32mp1/stm32mp1_common.c +++ b/plat/st/stm32mp1/stm32mp1_common.c @@ -43,6 +43,14 @@ MT_SECURE | \ MT_EXECUTE_NEVER) +#define MAP_DDR_NS MAP_REGION_FLAT(STM32MP1_DDR_BASE, \ + STM32MP1_DDR_MAX_SIZE, \ + MT_MEMORY | \ + MT_RW | \ + MT_NS | \ + MT_EXECUTE_NEVER) + +#if defined(IMAGE_BL2) static const mmap_region_t stm32mp1_mmap[] = { MAP_SRAM, MAP_DEVICE1, @@ -50,6 +58,16 @@ MAP_DDR, {0} }; +#endif +#if defined(IMAGE_BL32) +static const mmap_region_t stm32mp1_mmap[] = { + MAP_SRAM, + MAP_DEVICE1, + MAP_DEVICE2, + MAP_DDR_NS, + {0} +}; +#endif void configure_mmu(void) { diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h index 5ff509c..bb3fecf 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -71,7 +71,12 @@ * MAX_MMAP_REGIONS is usually: * BL stm32mp1_mmap size + mmap regions in *_plat_arch_setup */ -#define MAX_MMAP_REGIONS 11 +#if defined(IMAGE_BL2) + #define MAX_MMAP_REGIONS 11 +#endif +#if defined(IMAGE_BL32) + #define MAX_MMAP_REGIONS 6 +#endif /* DTB initialization value */ #define STM32MP1_DTB_SIZE U(0x00004000) /* 16Ko for DTB */ diff --git a/plat/st/stm32mp1/stm32mp1_gic.c b/plat/st/stm32mp1/stm32mp1_gic.c new file mode 100644 index 0000000..11eb0a3 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_gic.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include + +/****************************************************************************** + * On a GICv2 system, the Group 1 secure interrupts are treated as Group 0 + * interrupts. + *****************************************************************************/ +static const interrupt_prop_t stm32mp1_interrupt_props[] = { + PLATFORM_G1S_PROPS(GICV2_INTR_GROUP0), + PLATFORM_G0_PROPS(GICV2_INTR_GROUP0) +}; + +static unsigned int target_mask_array[PLATFORM_CORE_COUNT]; + +static const gicv2_driver_data_t platform_gic_data = { + .gicd_base = STM32MP1_GICD_BASE, + .gicc_base = STM32MP1_GICC_BASE, + .interrupt_props = stm32mp1_interrupt_props, + .interrupt_props_num = ARRAY_SIZE(stm32mp1_interrupt_props), + .target_masks = target_mask_array, + .target_masks_num = ARRAY_SIZE(target_mask_array), +}; + +void stm32mp1_gic_init(void) +{ + gicv2_driver_init(&platform_gic_data); + gicv2_distif_init(); + + stm32mp1_gic_pcpu_init(); +} + +void stm32mp1_gic_pcpu_init(void) +{ + gicv2_pcpu_distif_init(); + gicv2_set_pe_target_mask(plat_my_core_pos()); + gicv2_cpuif_enable(); +} diff --git a/plat/st/stm32mp1/stm32mp1_pm.c b/plat/st/stm32mp1/stm32mp1_pm.c new file mode 100644 index 0000000..e24af0e --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_pm.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint32_t stm32_sec_entrypoint; +static uint32_t cntfrq_core0; + +#define SEND_SECURE_IT_TO_CORE_1 0x20000U + +/******************************************************************************* + * STM32MP1 handler called when a CPU is about to enter standby. + * call by core 1 to enter in wfi + ******************************************************************************/ +static void stm32_cpu_standby(plat_local_state_t cpu_state) +{ + uint32_t interrupt = GIC_SPURIOUS_INTERRUPT; + + assert(cpu_state == ARM_LOCAL_STATE_RET); + + /* + * Enter standby state + * dsb is good practice before using wfi to enter low power states + */ + dsb(); + while (interrupt == GIC_SPURIOUS_INTERRUPT) { + wfi(); + + /* Acknoledge IT */ + interrupt = gicv2_acknowledge_interrupt(); + /* If Interrupt == 1022 it will be acknowledged by non secure */ + if ((interrupt != PENDING_G1_INTID) && + (interrupt != GIC_SPURIOUS_INTERRUPT)) { + gicv2_end_of_interrupt(interrupt); + } + } +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain is about to be turned on. The + * mpidr determines the CPU to be turned on. + * call by core 0 to activate core 1 + ******************************************************************************/ +static int stm32_pwr_domain_on(u_register_t mpidr) +{ + unsigned long current_cpu_mpidr = read_mpidr_el1(); + uint32_t tamp_clk_off = 0; + uint32_t bkpr_core1_addr = + tamp_bkpr(BOOT_API_CORE1_BRANCH_ADDRESS_TAMP_BCK_REG_IDX); + uint32_t bkpr_core1_magic = + tamp_bkpr(BOOT_API_CORE1_MAGIC_NUMBER_TAMP_BCK_REG_IDX); + + if (mpidr == current_cpu_mpidr) { + return PSCI_E_INVALID_PARAMS; + } + + if ((stm32_sec_entrypoint < STM32MP1_SRAM_BASE) || + (stm32_sec_entrypoint > (STM32MP1_SRAM_BASE + + (STM32MP1_SRAM_SIZE - 1)))) { + return PSCI_E_INVALID_ADDRESS; + } + + if (!stm32mp1_clk_is_enabled(RTCAPB)) { + tamp_clk_off = 1; + if (stm32mp1_clk_enable(RTCAPB) != 0) { + panic(); + } + } + + cntfrq_core0 = read_cntfrq_el0(); + + /* Write entrypoint in backup RAM register */ + mmio_write_32(bkpr_core1_addr, stm32_sec_entrypoint); + + /* Write magic number in backup register */ + mmio_write_32(bkpr_core1_magic, BOOT_API_A7_CORE1_MAGIC_NUMBER); + + if (tamp_clk_off != 0U) { + if (stm32mp1_clk_disable(RTCAPB) != 0) { + panic(); + } + } + + /* Generate an IT to core 1 */ + mmio_write_32(STM32MP1_GICD_BASE + GICD_SGIR, + SEND_SECURE_IT_TO_CORE_1 | ARM_IRQ_SEC_SGI_0); + + return PSCI_E_SUCCESS; +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain is about to be turned off. The + * target_state encodes the power state that each level should transition to. + ******************************************************************************/ +static void stm32_pwr_domain_off(const psci_power_state_t *target_state) +{ + /* Nothing to do */ +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain is about to be suspended. The + * target_state encodes the power state that each level should transition to. + ******************************************************************************/ +static void stm32_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + /* Nothing to do, power domain is not disabled */ +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain has just been powered on after + * being turned off earlier. The target_state encodes the low power state that + * each level has woken up from. + * call by core 1 just after wake up + ******************************************************************************/ +static void stm32_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + stm32mp1_gic_pcpu_init(); + + write_cntfrq_el0(cntfrq_core0); +} + +/******************************************************************************* + * STM32MP1 handler called when a power domain has just been powered on after + * having been suspended earlier. The target_state encodes the low power state + * that each level has woken up from. + ******************************************************************************/ +static void stm32_pwr_domain_suspend_finish(const psci_power_state_t + *target_state) +{ + /* Nothing to do, power domain is not disabled */ +} + +static void __dead2 stm32_pwr_domain_pwr_down_wfi(const psci_power_state_t + *target_state) +{ + ERROR("stm32mpu1 Power Down WFI: operation not handled.\n"); + panic(); +} + +static void __dead2 stm32_system_off(void) +{ + ERROR("stm32mpu1 System Off: operation not handled.\n"); + panic(); +} + +static void __dead2 stm32_system_reset(void) +{ + mmio_setbits_32(RCC_BASE + RCC_MP_GRSTCSETR, RCC_MP_GRSTCSETR_MPSYSRST); + + /* Loop in case system reset is not immediately caught */ + for ( ; ; ) { + ; + } +} + +static int stm32_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + int pstate = psci_get_pstate_type(power_state); + + if (pstate != 0) { + return PSCI_E_INVALID_PARAMS; + } + + if (psci_get_pstate_pwrlvl(power_state)) { + return PSCI_E_INVALID_PARAMS; + } + + if (psci_get_pstate_id(power_state)) { + return PSCI_E_INVALID_PARAMS; + } + + req_state->pwr_domain_state[0] = ARM_LOCAL_STATE_RET; + req_state->pwr_domain_state[1] = ARM_LOCAL_STATE_RUN; + + return PSCI_E_SUCCESS; +} + +static int stm32_validate_ns_entrypoint(uintptr_t entrypoint) +{ + /* The non-secure entry point must be in DDR */ + if (entrypoint < STM32MP1_DDR_BASE) { + return PSCI_E_INVALID_ADDRESS; + } + + return PSCI_E_SUCCESS; +} + +static int stm32_node_hw_state(u_register_t target_cpu, + unsigned int power_level) +{ + /* + * The format of 'power_level' is implementation-defined, but 0 must + * mean a CPU. Only allow level 0. + */ + if (power_level != MPIDR_AFFLVL0) { + return PSCI_E_INVALID_PARAMS; + } + + /* + * From psci view the CPU 0 is always ON, + * CPU 1 can be SUSPEND or RUNNING. + * Therefore do not manage POWER OFF state and always return HW_ON. + */ + + return (int)HW_ON; +} + +/******************************************************************************* + * Export the platform handlers. The ARM Standard platform layer will take care + * of registering the handlers with PSCI. + ******************************************************************************/ +static const plat_psci_ops_t stm32_psci_ops = { + .cpu_standby = stm32_cpu_standby, + .pwr_domain_on = stm32_pwr_domain_on, + .pwr_domain_off = stm32_pwr_domain_off, + .pwr_domain_suspend = stm32_pwr_domain_suspend, + .pwr_domain_on_finish = stm32_pwr_domain_on_finish, + .pwr_domain_suspend_finish = stm32_pwr_domain_suspend_finish, + .pwr_domain_pwr_down_wfi = stm32_pwr_domain_pwr_down_wfi, + .system_off = stm32_system_off, + .system_reset = stm32_system_reset, + .validate_power_state = stm32_validate_power_state, + .validate_ns_entrypoint = stm32_validate_ns_entrypoint, + .get_node_hw_state = stm32_node_hw_state +}; + +/******************************************************************************* + * Export the platform specific power ops. + ******************************************************************************/ +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) +{ + stm32_sec_entrypoint = sec_entrypoint; + *psci_ops = &stm32_psci_ops; + + return 0; +} diff --git a/plat/st/stm32mp1/stm32mp1_security.c b/plat/st/stm32mp1/stm32mp1_security.c index 0cdf305..e783c14 100644 --- a/plat/st/stm32mp1/stm32mp1_security.c +++ b/plat/st/stm32mp1/stm32mp1_security.c @@ -16,6 +16,46 @@ #include "platform_def.h" /******************************************************************************* + * Initialize the TrustZone Controller. Configure Region 0 with Secure RW access + * and allow Non-Secure masters full access. + ******************************************************************************/ +static void init_tzc400(void) +{ + unsigned long long region_base, region_top; + unsigned long long ddr_base = STM32MP1_DDR_BASE; + unsigned long long ddr_size = (unsigned long long)dt_get_ddr_size(); + + tzc400_init(STM32MP1_TZC_BASE); + + tzc400_disable_filters(); + + /* Region 1 set to cover all DRAM at 0xC000_0000. Apply the + * same configuration to all filters in the TZC. + */ + region_base = ddr_base; + region_top = ddr_base + (ddr_size - 1U); + tzc400_configure_region(STM32MP1_FILTER_BIT_ALL, 1, + region_base, + region_top, + TZC_REGION_S_RDWR, + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_A7_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_GPU_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_LCD_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_MDMA_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_DMA_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_USB_HOST_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_USB_OTG_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_SDMMC_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_ETH_ID) | + TZC_REGION_ACCESS_RDWR(STM32MP1_TZC_DAP_ID)); + + /* Raise an exception if a NS device tries to access secure memory */ + tzc400_set_action(TZC_ACTION_ERR); + + tzc400_enable_filters(); +} + +/******************************************************************************* * Initialize the TrustZone Controller. * Early initialization create only one region with full access to secure. * This setting is used before and during DDR initialization. @@ -76,3 +116,12 @@ { early_init_tzc400(); } + +/******************************************************************************* + * Initialize the secure environment. At this moment only the TrustZone + * Controller is initialized. + ******************************************************************************/ +void stm32mp1_security_setup(void) +{ + init_tzc400(); +} diff --git a/plat/st/stm32mp1/stm32mp1_topology.c b/plat/st/stm32mp1/stm32mp1_topology.c new file mode 100644 index 0000000..405aa33 --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_topology.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +/* 1 cluster, all cores into */ +static const unsigned char stm32mp1_power_domain_tree_desc[] = { + PLATFORM_CLUSTER_COUNT, + PLATFORM_CORE_COUNT, +}; + +/* This function returns the platform topology */ +const unsigned char *plat_get_power_domain_tree_desc(void) +{ + return stm32mp1_power_domain_tree_desc; +} + +/******************************************************************************* + * This function implements a part of the critical interface between the psci + * generic layer and the platform that allows the former to query the platform + * to convert an MPIDR to a unique linear index. An error code (-1) is returned + * in case the MPIDR is invalid. + ******************************************************************************/ +int plat_core_pos_by_mpidr(u_register_t mpidr) +{ + unsigned int cluster_id, cpu_id; + u_register_t mpidr_copy = mpidr; + + mpidr_copy &= MPIDR_AFFINITY_MASK; + + if ((mpidr_copy & ~(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK)) != 0U) { + return -1; + } + + cluster_id = (mpidr_copy >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; + cpu_id = (mpidr_copy >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; + + if (cluster_id >= PLATFORM_CLUSTER_COUNT) { + return -1; + } + + /* + * Validate cpu_id by checking whether it represents a CPU in one + * of the two clusters present on the platform. + */ + if (cpu_id >= PLATFORM_CORE_COUNT) { + return -1; + } + + return (int)cpu_id; +}