arm-trusted-firmware/plat/imx/common/imx_sip_handler.c

256 lines
5.0 KiB
C
Raw Normal View History

/*
* 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>
feat(plat/imx8m): add SiP call for secondary boot In iMX8MM 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. To trigger that switch the PERSIST_SECONDARY_BOOT bit should be set in GPR10 SRC register. As the bit is retained after WARM reset, that permits to control BootROM behavior regarding what boot image it will boot after reset: primary or secondary. This is useful for reliable bootloader A/B updates, as it permits switching between two copies of bootloader at different offsets of the same storage. If the PERSIST_SECONDARY_BOOT is 0, the boot ROM uses address 0x8400 for the primary image. If the PERSIST_SECONDARY_BOOT is 1, the boot ROM reads that secondary image table from address 0x8200 on the boot media and uses the address specified in the table for the secondary image. Secondary Image Table contains the sector of secondary bootloader image, exluding the offset to that image (explained below in the note). To generate the Secondary Image Table, use e.g.: $ printf '\x0\x0\x0\x0\x0\x0\x0\x0\x33\x22\x11' '\x00\x00\x10\x0\x0\x00\x0\x0\x0' > /tmp/sit.bin $ hexdump -vC /tmp/sit.bin 00000000 00 00 00 00 00000004 00 00 00 00 00000008 33 22 11 00 <--- This is the "tag" 0000000c 00 10 00 00 <--- This is the "firstSectorNumber" 00000010 00 00 00 00 You can also use NXP script from [1][2] imx-mkimage tool for SIT generation. Note that the firstSectorNumber is NOT the offset of the IVT, but an offset of the IVT decremented by Image Vector Table offset (Table 6-25. Image Vector Table Offset and Initial Load Region Size for iMX8MM/MQ), so for secondary SPL copy at offset 0x1042 sectors, firstSectorNumber must be 0x1000 (0x42 sectors * 512 = 0x8400 bytes offset). In order to test redundant boot board should be closed and SD/MMC manufacture mode disabled, as secondary boot is not supported in the SD/MMC manufacture mode, which can be disabled by blowing DISABLE_SDMMC_MFG (example for iMX8MM): > fuse prog -y 2 1 0x00800000 For additional details check i.MX 8M Mini Apllication Processor Reference Manual, 6.1.5.4.5 Redundant boot support for expansion device chapter. [1] https://source.codeaurora.org/external/imx/imx-mkimage/ [2] scripts/gen_sit.sh Change-Id: I0a5cea7295a4197f6c89183d74b4011cada52d4c Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
2021-03-10 11:42:55 +00:00
#include <lib/mmio.h>
#include <sci/sci.h>
#if defined(PLAT_imx8qm) || defined(PLAT_imx8qx)
#ifdef PLAT_imx8qm
const static 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) */
feat(plat/imx8m): add SiP call for secondary boot In iMX8MM 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. To trigger that switch the PERSIST_SECONDARY_BOOT bit should be set in GPR10 SRC register. As the bit is retained after WARM reset, that permits to control BootROM behavior regarding what boot image it will boot after reset: primary or secondary. This is useful for reliable bootloader A/B updates, as it permits switching between two copies of bootloader at different offsets of the same storage. If the PERSIST_SECONDARY_BOOT is 0, the boot ROM uses address 0x8400 for the primary image. If the PERSIST_SECONDARY_BOOT is 1, the boot ROM reads that secondary image table from address 0x8200 on the boot media and uses the address specified in the table for the secondary image. Secondary Image Table contains the sector of secondary bootloader image, exluding the offset to that image (explained below in the note). To generate the Secondary Image Table, use e.g.: $ printf '\x0\x0\x0\x0\x0\x0\x0\x0\x33\x22\x11' '\x00\x00\x10\x0\x0\x00\x0\x0\x0' > /tmp/sit.bin $ hexdump -vC /tmp/sit.bin 00000000 00 00 00 00 00000004 00 00 00 00 00000008 33 22 11 00 <--- This is the "tag" 0000000c 00 10 00 00 <--- This is the "firstSectorNumber" 00000010 00 00 00 00 You can also use NXP script from [1][2] imx-mkimage tool for SIT generation. Note that the firstSectorNumber is NOT the offset of the IVT, but an offset of the IVT decremented by Image Vector Table offset (Table 6-25. Image Vector Table Offset and Initial Load Region Size for iMX8MM/MQ), so for secondary SPL copy at offset 0x1042 sectors, firstSectorNumber must be 0x1000 (0x42 sectors * 512 = 0x8400 bytes offset). In order to test redundant boot board should be closed and SD/MMC manufacture mode disabled, as secondary boot is not supported in the SD/MMC manufacture mode, which can be disabled by blowing DISABLE_SDMMC_MFG (example for iMX8MM): > fuse prog -y 2 1 0x00800000 For additional details check i.MX 8M Mini Apllication Processor Reference Manual, 6.1.5.4.5 Redundant boot support for expansion device chapter. [1] https://source.codeaurora.org/external/imx/imx-mkimage/ [2] scripts/gen_sit.sh Change-Id: I0a5cea7295a4197f6c89183d74b4011cada52d4c Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
2021-03-10 11:42:55 +00:00
#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) */
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;
}