mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-22 20:38:03 +00:00

In i.MX8MM/MQ it is possible to have two copies of bootloader in SD/eMMC and switch between them. The switch is triggered either by the BootROM in case the bootloader image is faulty OR can be enforced by the user, and there is API introduced in9ce232fe
("feat(plat/imx8m): add SiP call for secondary boot"), which leverages this SoC feature. However neither i.MX8MP nor i.MX8MN have a dedicated bit which indicates what boot image set is currently booted. According to AN12853 [1] "i.MX ROMs Log Events", it is possible to determine whether fallback event occurred by parsing the BootROM event log. In case ROM event ID 0x51 is present,fallback event did occur and secondary boot image was booted. Knowing which boot image was booted might be useful for reliable bootloader A/B updates, detecting fallback event might be used for making decision if boot firmware rollback is required. This patche introduces implementation, that replicates the same imx_src_handler() behaviour as on i.MX8MM/MQ SoCs. The code is based on original U-Boot implementation [2]. [1]: https://www.nxp.com/webapp/Download?colCode=AN12853 [2]:a5ee05cf71
Change-Id: I9a4c5229aa0e53fa23b5261459da99cb3ce6bdbe Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
334 lines
7 KiB
C
334 lines
7 KiB
C
/*
|
|
* Copyright 2019 NXP
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <arch.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <services/std_svc.h>
|
|
#include <string.h>
|
|
#include <platform_def.h>
|
|
#include <common/debug.h>
|
|
#include <common/runtime_svc.h>
|
|
#include <imx_sip_svc.h>
|
|
#include <lib/el3_runtime/context_mgmt.h>
|
|
#include <lib/mmio.h>
|
|
#include <sci/sci.h>
|
|
|
|
#if defined(PLAT_imx8mn) || defined(PLAT_imx8mp)
|
|
/*
|
|
* Defined in
|
|
* table 11. ROM event log buffer address location
|
|
* AN12853 "i.MX ROMs Log Events"
|
|
*/
|
|
#define ROM_LOG_BUFFER_ADDR 0x9E0
|
|
#endif
|
|
|
|
#if defined(PLAT_imx8qm) || defined(PLAT_imx8qx)
|
|
|
|
#ifdef PLAT_imx8qm
|
|
static const int ap_cluster_index[PLATFORM_CLUSTER_COUNT] = {
|
|
SC_R_A53, SC_R_A72,
|
|
};
|
|
#endif
|
|
|
|
static int imx_srtc_set_time(uint32_t year_mon,
|
|
unsigned long day_hour,
|
|
unsigned long min_sec)
|
|
{
|
|
return sc_timer_set_rtc_time(ipc_handle,
|
|
year_mon >> 16, year_mon & 0xffff,
|
|
day_hour >> 16, day_hour & 0xffff,
|
|
min_sec >> 16, min_sec & 0xffff);
|
|
}
|
|
|
|
int imx_srtc_handler(uint32_t smc_fid,
|
|
void *handle,
|
|
u_register_t x1,
|
|
u_register_t x2,
|
|
u_register_t x3,
|
|
u_register_t x4)
|
|
{
|
|
int ret;
|
|
|
|
switch (x1) {
|
|
case IMX_SIP_SRTC_SET_TIME:
|
|
ret = imx_srtc_set_time(x2, x3, x4);
|
|
break;
|
|
default:
|
|
ret = SMC_UNK;
|
|
}
|
|
|
|
SMC_RET1(handle, ret);
|
|
}
|
|
|
|
static void imx_cpufreq_set_target(uint32_t cluster_id, unsigned long freq)
|
|
{
|
|
sc_pm_clock_rate_t rate = (sc_pm_clock_rate_t)freq;
|
|
|
|
#ifdef PLAT_imx8qm
|
|
sc_pm_set_clock_rate(ipc_handle, ap_cluster_index[cluster_id], SC_PM_CLK_CPU, &rate);
|
|
#endif
|
|
#ifdef PLAT_imx8qx
|
|
sc_pm_set_clock_rate(ipc_handle, SC_R_A35, SC_PM_CLK_CPU, &rate);
|
|
#endif
|
|
}
|
|
|
|
int imx_cpufreq_handler(uint32_t smc_fid,
|
|
u_register_t x1,
|
|
u_register_t x2,
|
|
u_register_t x3)
|
|
{
|
|
switch (x1) {
|
|
case IMX_SIP_SET_CPUFREQ:
|
|
imx_cpufreq_set_target(x2, x3);
|
|
break;
|
|
default:
|
|
return SMC_UNK;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool wakeup_src_irqsteer;
|
|
|
|
bool imx_is_wakeup_src_irqsteer(void)
|
|
{
|
|
return wakeup_src_irqsteer;
|
|
}
|
|
|
|
int imx_wakeup_src_handler(uint32_t smc_fid,
|
|
u_register_t x1,
|
|
u_register_t x2,
|
|
u_register_t x3)
|
|
{
|
|
switch (x1) {
|
|
case IMX_SIP_WAKEUP_SRC_IRQSTEER:
|
|
wakeup_src_irqsteer = true;
|
|
break;
|
|
case IMX_SIP_WAKEUP_SRC_SCU:
|
|
wakeup_src_irqsteer = false;
|
|
break;
|
|
default:
|
|
return SMC_UNK;
|
|
}
|
|
|
|
return SMC_OK;
|
|
}
|
|
|
|
int imx_otp_handler(uint32_t smc_fid,
|
|
void *handle,
|
|
u_register_t x1,
|
|
u_register_t x2)
|
|
{
|
|
int ret;
|
|
uint32_t fuse;
|
|
|
|
switch (smc_fid) {
|
|
case IMX_SIP_OTP_READ:
|
|
ret = sc_misc_otp_fuse_read(ipc_handle, x1, &fuse);
|
|
SMC_RET2(handle, ret, fuse);
|
|
break;
|
|
case IMX_SIP_OTP_WRITE:
|
|
ret = sc_misc_otp_fuse_write(ipc_handle, x1, x2);
|
|
SMC_RET1(handle, ret);
|
|
break;
|
|
default:
|
|
ret = SMC_UNK;
|
|
SMC_RET1(handle, ret);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int imx_misc_set_temp_handler(uint32_t smc_fid,
|
|
u_register_t x1,
|
|
u_register_t x2,
|
|
u_register_t x3,
|
|
u_register_t x4)
|
|
{
|
|
return sc_misc_set_temp(ipc_handle, x1, x2, x3, x4);
|
|
}
|
|
|
|
#endif /* defined(PLAT_imx8qm) || defined(PLAT_imx8qx) */
|
|
|
|
#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq)
|
|
int imx_src_handler(uint32_t smc_fid,
|
|
u_register_t x1,
|
|
u_register_t x2,
|
|
u_register_t x3,
|
|
void *handle)
|
|
{
|
|
uint32_t val;
|
|
|
|
switch (x1) {
|
|
case IMX_SIP_SRC_SET_SECONDARY_BOOT:
|
|
if (x2 != 0U) {
|
|
mmio_setbits_32(IMX_SRC_BASE + SRC_GPR10_OFFSET,
|
|
SRC_GPR10_PERSIST_SECONDARY_BOOT);
|
|
} else {
|
|
mmio_clrbits_32(IMX_SRC_BASE + SRC_GPR10_OFFSET,
|
|
SRC_GPR10_PERSIST_SECONDARY_BOOT);
|
|
}
|
|
break;
|
|
case IMX_SIP_SRC_IS_SECONDARY_BOOT:
|
|
val = mmio_read_32(IMX_SRC_BASE + SRC_GPR10_OFFSET);
|
|
return !!(val & SRC_GPR10_PERSIST_SECONDARY_BOOT);
|
|
default:
|
|
return SMC_UNK;
|
|
|
|
};
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(PLAT_imx8mm) || defined(PLAT_imx8mq) */
|
|
|
|
#if defined(PLAT_imx8mn) || defined(PLAT_imx8mp)
|
|
static bool is_secondary_boot(void)
|
|
{
|
|
uint32_t *rom_log_addr = (uint32_t *)ROM_LOG_BUFFER_ADDR;
|
|
bool is_secondary = false;
|
|
uint32_t *rom_log;
|
|
uint8_t event_id;
|
|
|
|
/* If the ROM event log pointer is not valid. */
|
|
if (*rom_log_addr < 0x900000 || *rom_log_addr >= 0xB00000 ||
|
|
*rom_log_addr & 0x3) {
|
|
return false;
|
|
}
|
|
|
|
/* Parse the ROM event ID version 2 log */
|
|
rom_log = (uint32_t *)(uintptr_t)(*rom_log_addr);
|
|
for (size_t i = 0; i < 128; i++) {
|
|
event_id = rom_log[i] >> 24;
|
|
switch (event_id) {
|
|
case 0x00: /* End of list */
|
|
return is_secondary;
|
|
/* Log entries with 1 parameter, skip 1 */
|
|
case 0x80: /* Perform the device initialization */
|
|
case 0x81: /* The boot device initialization completes */
|
|
case 0x82: /* Execute boot device driver pre-config */
|
|
case 0x8F: /* The boot device initialization fails */
|
|
case 0x90: /* Start to read data from boot device */
|
|
case 0x91: /* Reading data from boot device completes */
|
|
case 0x9F: /* Reading data from boot device fails */
|
|
i += 1;
|
|
continue;
|
|
/* Log entries with 2 parameters, skip 2 */
|
|
case 0xA0: /* Image authentication result */
|
|
case 0xC0: /* Jump to the boot image soon */
|
|
i += 2;
|
|
continue;
|
|
/* Booted the primary boot image */
|
|
case 0x50:
|
|
is_secondary = false;
|
|
continue;
|
|
/* Booted the secondary boot image */
|
|
case 0x51:
|
|
is_secondary = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return is_secondary;
|
|
}
|
|
|
|
int imx_src_handler(uint32_t smc_fid,
|
|
u_register_t x1,
|
|
u_register_t x2,
|
|
u_register_t x3,
|
|
void *handle)
|
|
{
|
|
switch (x1) {
|
|
case IMX_SIP_SRC_SET_SECONDARY_BOOT:
|
|
/* we do support that on these SoCs */
|
|
break;
|
|
case IMX_SIP_SRC_IS_SECONDARY_BOOT:
|
|
return is_secondary_boot();
|
|
default:
|
|
return SMC_UNK;
|
|
};
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(PLAT_imx8mn) || defined(PLAT_imx8mp) */
|
|
|
|
static uint64_t imx_get_commit_hash(u_register_t x2,
|
|
u_register_t x3,
|
|
u_register_t x4)
|
|
{
|
|
/* Parse the version_string */
|
|
char *parse = (char *)version_string;
|
|
uint64_t hash = 0;
|
|
|
|
do {
|
|
parse = strchr(parse, '-');
|
|
if (parse) {
|
|
parse += 1;
|
|
if (*(parse) == 'g') {
|
|
/* Default is 7 hexadecimal digits */
|
|
memcpy((void *)&hash, (void *)(parse + 1), 7);
|
|
break;
|
|
}
|
|
}
|
|
|
|
} while (parse != NULL);
|
|
|
|
return hash;
|
|
}
|
|
|
|
uint64_t imx_buildinfo_handler(uint32_t smc_fid,
|
|
u_register_t x1,
|
|
u_register_t x2,
|
|
u_register_t x3,
|
|
u_register_t x4)
|
|
{
|
|
uint64_t ret;
|
|
|
|
switch (x1) {
|
|
case IMX_SIP_BUILDINFO_GET_COMMITHASH:
|
|
ret = imx_get_commit_hash(x2, x3, x4);
|
|
break;
|
|
default:
|
|
return SMC_UNK;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int imx_kernel_entry_handler(uint32_t smc_fid,
|
|
u_register_t x1,
|
|
u_register_t x2,
|
|
u_register_t x3,
|
|
u_register_t x4)
|
|
{
|
|
static entry_point_info_t bl33_image_ep_info;
|
|
entry_point_info_t *next_image_info;
|
|
unsigned int mode;
|
|
|
|
if (x1 < (PLAT_NS_IMAGE_OFFSET & 0xF0000000))
|
|
return SMC_UNK;
|
|
|
|
mode = MODE32_svc;
|
|
|
|
next_image_info = &bl33_image_ep_info;
|
|
|
|
next_image_info->pc = x1;
|
|
|
|
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->args.arg0 = 0;
|
|
next_image_info->args.arg1 = 0;
|
|
next_image_info->args.arg2 = x3;
|
|
|
|
SET_SECURITY_STATE(next_image_info->h.attr, NON_SECURE);
|
|
|
|
cm_init_my_context(next_image_info);
|
|
cm_prepare_el3_exit(NON_SECURE);
|
|
|
|
return 0;
|
|
}
|