blob: ff585fe3dd66e7b638a4d3f95471be50d9a6a228 [file] [log] [blame]
/*
* Copyright (c) 2022, Mediatek Inc. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
#include <common/debug.h>
#include <common/runtime_svc.h>
#include <lib/el3_runtime/context_mgmt.h>
/* Vendors headers */
#include <cold_boot.h>
#include <lib/mtk_init/mtk_init.h>
#include <mtk_sip_svc.h>
static struct kernel_info k_info;
static entry_point_info_t bl32_image_ep_info;
static entry_point_info_t bl33_image_ep_info;
static bool el1_is_2nd_bootloader = true;
static struct atf_arg_t atfarg;
static int init_mtk_bl32_arg(void)
{
struct mtk_bl_param_t *p_mtk_bl_param;
struct atf_arg_t *p_atfarg;
p_mtk_bl_param = (struct mtk_bl_param_t *) get_mtk_bl31_fw_config(BOOT_ARG_FROM_BL2);
if (p_mtk_bl_param == NULL) {
ERROR("p_mtk_bl_param is NULL!\n");
return -1;
}
p_atfarg = (struct atf_arg_t *)p_mtk_bl_param->atf_arg_addr;
if (p_atfarg == NULL) {
ERROR("bl32 argument is NULL!\n");
return -1;
}
memcpy((void *)&atfarg, (void *)p_atfarg, sizeof(struct atf_arg_t));
return 0;
}
MTK_EARLY_PLAT_INIT(init_mtk_bl32_arg);
static void save_kernel_info(uint64_t pc, uint64_t r0, uint64_t r1, uint64_t k32_64)
{
k_info.k32_64 = k32_64;
k_info.pc = pc;
if (k32_64 == LINUX_KERNEL_32) {
/* for 32 bits kernel */
k_info.r0 = 0;
/* machtype */
k_info.r1 = r0;
/* tags */
k_info.r2 = r1;
} else {
/* for 64 bits kernel */
k_info.r0 = r0;
k_info.r1 = r1;
}
}
static uint32_t plat_get_spsr_for_bl32_64_entry(void)
{
return SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
}
#if MTK_BL33_IS_64BIT
static uint32_t plat_get_spsr_for_bl33_entry(void)
{
uint32_t spsr;
uint32_t mode;
mode = MODE_EL1;
spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
return spsr;
}
#else
static uint32_t plat_get_spsr_for_bl33_entry(void)
{
unsigned int mode;
uint32_t spsr;
unsigned int ee;
unsigned long daif;
INFO("Secondary bootloader is AArch32\n");
mode = MODE32_svc;
ee = 0;
/*
* TODO: Choose async. exception bits if HYP mode is not
* implemented according to the values of SCR.{AW, FW} bits
*/
daif = DAIF_ABT_BIT | DAIF_IRQ_BIT | DAIF_FIQ_BIT;
spsr = SPSR_MODE32(mode, 0, ee, daif);
return spsr;
}
#endif
static void populate_bl32_image_ep(entry_point_info_t *bl32_ep_instance,
struct mtk_bl_param_t *p_mtk_bl_param)
{
entry_point_info_t *populated_ep_bl32 = bl32_ep_instance;
if (p_mtk_bl_param == NULL) {
ERROR("p_mtk_bl_param is NULL!\n");
panic();
}
SET_SECURITY_STATE(bl32_ep_instance->h.attr, SECURE);
SET_PARAM_HEAD(populated_ep_bl32,
PARAM_EP,
VERSION_1,
populated_ep_bl32->h.attr);
populated_ep_bl32->pc = atfarg.tee_entry;
populated_ep_bl32->spsr = plat_get_spsr_for_bl32_64_entry();
}
static void populate_bl33_image_ep(entry_point_info_t *bl33_ep_instance,
struct mtk_bl_param_t *p_mtk_bl_param)
{
entry_point_info_t *populated_ep_bl33 = bl33_ep_instance;
if (p_mtk_bl_param == NULL) {
ERROR("p_mtk_bl_param is NULL!\n");
panic();
}
SET_SECURITY_STATE(bl33_ep_instance->h.attr, NON_SECURE);
SET_PARAM_HEAD(populated_ep_bl33,
PARAM_EP,
VERSION_1,
populated_ep_bl33->h.attr);
populated_ep_bl33->pc = p_mtk_bl_param->bl33_start_addr;
/* standardize 2nd bootloader input argument */
populated_ep_bl33->args.arg0 = p_mtk_bl_param->bootarg_loc;
/* compatible to old GZ version */
populated_ep_bl33->args.arg4 = p_mtk_bl_param->bootarg_loc;
populated_ep_bl33->args.arg5 = p_mtk_bl_param->bootarg_size;
populated_ep_bl33->spsr = plat_get_spsr_for_bl33_entry();
}
static int populate_bl_images_ep(struct mtk_bl_param_t *p_mtk_bl_param)
{
/*
* Tell BL31 where the non-trusted software image
* is located and the entry state information
*/
populate_bl33_image_ep(&bl33_image_ep_info, p_mtk_bl_param);
populate_bl32_image_ep(&bl32_image_ep_info, p_mtk_bl_param);
return 0;
}
static int populate_bl_images_ep_init(void)
{
return populate_bl_images_ep(get_mtk_bl31_fw_config(BOOT_ARG_FROM_BL2));
}
MTK_PLAT_SETUP_0_INIT(populate_bl_images_ep_init);
static entry_point_info_t *bl31_plat_get_next_kernel64_ep_info(void)
{
entry_point_info_t *next_image_info;
unsigned long el_status;
unsigned int mode;
el_status = 0;
mode = 0;
/* Kernel image is always non-secured */
next_image_info = &bl33_image_ep_info;
/* Figure out what mode we enter the non-secure world in */
el_status = read_id_aa64pfr0_el1() >> ID_AA64PFR0_EL2_SHIFT;
el_status &= ID_AA64PFR0_ELX_MASK;
INFO("Kernel_EL %d\n", el_status?2:1);
if (el_status) {
mode = MODE_EL2;
} else {
mode = MODE_EL1;
}
INFO("Kernel is 64Bit\n");
next_image_info->spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
next_image_info->pc = k_info.pc;
next_image_info->args.arg0 = k_info.r0;
next_image_info->args.arg1 = k_info.r1;
INFO("pc=0x%lx, r0=0x%" PRIx64 ", r1=0x%" PRIx64 "\n",
next_image_info->pc,
next_image_info->args.arg0,
next_image_info->args.arg1);
SET_SECURITY_STATE(next_image_info->h.attr, NON_SECURE);
/* None of the images on this platform can have 0x0 as the entrypoint */
if (next_image_info->pc) {
return next_image_info;
}
return NULL;
}
static entry_point_info_t *bl31_plat_get_next_kernel32_ep_info(void)
{
entry_point_info_t *next_image_info;
unsigned int mode;
mode = 0;
/* Kernel image is always non-secured */
next_image_info = &bl33_image_ep_info;
/* Figure out what mode we enter the non-secure world in */
mode = MODE32_hyp;
/*
* TODO: Consider the possibility of specifying the SPSR in
* the FIP ToC and allowing the platform to have a say as
* well.
*/
INFO("Kernel is 32Bit\n");
next_image_info->spsr = SPSR_MODE32(mode, SPSR_T_ARM, SPSR_E_LITTLE,
(DAIF_FIQ_BIT | DAIF_IRQ_BIT | DAIF_ABT_BIT));
next_image_info->pc = k_info.pc;
next_image_info->args.arg0 = k_info.r0;
next_image_info->args.arg1 = k_info.r1;
next_image_info->args.arg2 = k_info.r2;
INFO("pc=0x%lx, r0=0x%" PRIx64 ", r1=0x%" PRIx64 ", r2=0x%" PRIx64 "\n",
next_image_info->pc,
next_image_info->args.arg0,
next_image_info->args.arg1,
next_image_info->args.arg2);
SET_SECURITY_STATE(next_image_info->h.attr, NON_SECURE);
/* None of the images on this platform can have 0x0 as the entrypoint */
if (next_image_info->pc) {
return next_image_info;
}
return NULL;
}
static void bl31_prepare_kernel_entry(uint64_t k32_64)
{
entry_point_info_t *next_image_info = NULL;
uint32_t image_type;
/* Determine which image to execute next */
image_type = NON_SECURE; /* bl31_get_next_image_type(); */
/* Leave 2nd bootloader then jump to kernel */
el1_is_2nd_bootloader = false;
/* Program EL3 registers to enable entry into the next EL */
if (k32_64 == LINUX_KERNEL_32) {
next_image_info = bl31_plat_get_next_kernel32_ep_info();
} else {
next_image_info = bl31_plat_get_next_kernel64_ep_info();
}
assert(next_image_info);
assert(image_type == GET_SECURITY_STATE(next_image_info->h.attr));
INFO("BL31: Preparing for EL3 exit to %s world, Kernel\n",
(image_type == SECURE) ? "secure" : "normal");
INFO("BL31: Next image address = 0x%" PRIx64 "\n",
next_image_info->pc);
INFO("BL31: Next image spsr = 0x%x\n", next_image_info->spsr);
cm_init_my_context(next_image_info);
cm_prepare_el3_exit(image_type);
}
bool is_el1_2nd_bootloader(void)
{
return el1_is_2nd_bootloader;
}
/*******************************************************************************
* 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;
next_image_info = (type == NON_SECURE) ? &bl33_image_ep_info : &bl32_image_ep_info;
/* None of the images on this platform can have 0x0 as the entrypoint */
if (next_image_info->pc) {
return next_image_info;
}
return NULL;
}
u_register_t boot_to_kernel(u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *handle,
struct smccc_res *smccc_ret)
{
static uint8_t kernel_boot_once_flag;
/* only support in booting flow */
if (kernel_boot_once_flag == 0) {
kernel_boot_once_flag = 1;
INFO("save kernel info\n");
save_kernel_info(x1, x2, x3, x4);
bl31_prepare_kernel_entry(x4);
INFO("el3_exit\n");
/*
* FIXME: no better way so far to prevent from
* SiP root handler wipe x0~x3 if not assign smccc_ret
* return register
*/
smccc_ret->a1 = x3;
mtk_init_one_level(MTK_INIT_LVL_BL33_DEFER);
#if MTK_CONSOLE_RUNTIME_DISABLE
INFO("Turn off BL31 console\n");
mtk_console_core_end();
#endif
/* Re-assign as x0 register entering Linux kernel */
return x2;
}
return 0;
}
/* Register SiP SMC service */
DECLARE_SMC_HANDLER(MTK_SIP_KERNEL_BOOT, boot_to_kernel);