stm32mp1: Add BL32 SP_min secure monitor

Signed-off-by: Yann Gautier <yann.gautier@st.com>
Signed-off-by: Mathieu Belou <mathieu.belou@st.com>
Signed-off-by: Lionel Debieve <lionel.debieve@st.com>
Signed-off-by: Etienne Carriere <etienne.carriere@st.com>
Signed-off-by: Nicolas Le Bayon <nicolas.le.bayon@st.com>
This commit is contained in:
Yann Gautier 2018-07-16 19:36:06 +02:00
parent 10a511ceaa
commit 964dfee1a2
10 changed files with 605 additions and 1 deletions

View File

@ -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"

View File

@ -11,8 +11,12 @@ void stm32mp1_io_setup(void);
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__ */

View File

@ -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

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <arm_gic.h>
#include <assert.h>
#include <bl_common.h>
#include <console.h>
#include <context.h>
#include <context_mgmt.h>
#include <debug.h>
#include <dt-bindings/clock/stm32mp1-clks.h>
#include <generic_delay_timer.h>
#include <mmio.h>
#include <platform.h>
#include <platform_def.h>
#include <platform_sp_min.h>
#include <stm32mp1_clk.h>
#include <stm32mp1_dt.h>
#include <stm32mp1_private.h>
#include <string.h>
#include <tzc400.h>
#include <xlat_tables_v2.h>
/******************************************************************************
* 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)
{
}

View File

@ -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 @@ static const mmap_region_t stm32mp1_mmap[] = {
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)
{

View File

@ -71,7 +71,12 @@ enum ddr_type {
* 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 */

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <bl_common.h>
#include <gicv2.h>
#include <platform.h>
#include <platform_def.h>
#include <utils.h>
#include <stm32mp1_private.h>
/******************************************************************************
* 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();
}

View File

@ -0,0 +1,255 @@
/*
* Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <assert.h>
#include <boot_api.h>
#include <debug.h>
#include <dt-bindings/clock/stm32mp1-clks.h>
#include <errno.h>
#include <gic_common.h>
#include <gicv2.h>
#include <mmio.h>
#include <platform_def.h>
#include <platform.h>
#include <psci.h>
#include <stm32mp1_clk.h>
#include <stm32mp1_private.h>
#include <stm32mp1_rcc.h>
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;
}

View File

@ -15,6 +15,46 @@
#include <tzc400.h>
#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.
@ -76,3 +116,12 @@ void stm32mp1_arch_security_setup(void)
{
early_init_tzc400();
}
/*******************************************************************************
* Initialize the secure environment. At this moment only the TrustZone
* Controller is initialized.
******************************************************************************/
void stm32mp1_security_setup(void)
{
init_tzc400();
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <platform_def.h>
#include <platform.h>
#include <psci.h>
/* 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;
}