diff --git a/fel.c b/fel.c index 295d5d8..dce8de1 100644 --- a/fel.c +++ b/fel.c @@ -402,6 +402,39 @@ void aw_set_sctlr(feldev_handle *dev, soc_info_t *soc_info, aw_write_arm_cp_reg(dev, soc_info, 15, 0, 1, 0, 0, sctlr); } +/* + * Issue a "smc #0" instruction. This brings a SoC booted in "secure boot" + * state from the default non-secure FEL into secure FEL. + * This crashes on devices using "non-secure boot", as the BROM does not + * provide a handler address in MVBAR. So we have a runtime check. + */ +void aw_apply_smc_workaround(feldev_handle *dev) +{ + soc_info_t *soc_info = dev->soc_info; + uint32_t val; + uint32_t arm_code[] = { + htole32(0xe1600070), /* smc #0 */ + htole32(0xe12fff1e), /* bx lr */ + }; + + /* Return if the SoC does not need this workaround */ + if (!soc_info->needs_smc_workaround_if_zero_word_at_addr) + return; + + /* This has less overhead than fel_readl_n() and may be good enough */ + aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr, + &val, sizeof(val)); + + /* Return if the workaround is not needed or has been already applied */ + if (val != 0) + return; + + pr_info("Applying SMC workaround... "); + aw_fel_write(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); + aw_fel_execute(dev, soc_info->scratch_addr); + pr_info(" done.\n"); +} + /* * Reconstruct the same MMU translation table as used by the A20 BROM. * We are basically reverting the changes, introduced in newer SoC @@ -1092,6 +1125,9 @@ int main(int argc, char **argv) */ handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID); + /* Some SoCs need the SMC workaround to enter the secure boot mode */ + aw_apply_smc_workaround(handle); + /* Handle command-style arguments, in order of appearance */ while (argc > 1 ) { int skip = 1; diff --git a/soc_info.c b/soc_info.c index 189e9bd..7728422 100644 --- a/soc_info.c +++ b/soc_info.c @@ -148,6 +148,8 @@ soc_info_t soc_info_table[] = { .sid_base = 0x01C14000, .sid_offset = 0x200, .rvbar_reg = 0x017000A0, + /* Check L.NOP in the OpenRISC reset vector */ + .needs_smc_workaround_if_zero_word_at_addr = 0x40004, },{ .soc_id = 0x1639, /* Allwinner A80 */ .name = "A80", @@ -175,6 +177,8 @@ soc_info_t soc_info_table[] = { .sid_base = 0x01C14000, .sid_offset = 0x200, .sid_fix = true, + /* Check L.NOP in the OpenRISC reset vector */ + .needs_smc_workaround_if_zero_word_at_addr = 0x40004, },{ .soc_id = 0x1681, /* Allwinner V3s */ .name = "V3s", @@ -193,6 +197,8 @@ soc_info_t soc_info_table[] = { .sid_base = 0x01C14000, .sid_offset = 0x200, .rvbar_reg = 0x017000A0, + /* Check L.NOP in the OpenRISC reset vector */ + .needs_smc_workaround_if_zero_word_at_addr = 0x40004, },{ .soc_id = 0x1701, /* Allwinner R40 */ .name = "R40", diff --git a/soc_info.h b/soc_info.h index 53fbb55..d424c66 100644 --- a/soc_info.h +++ b/soc_info.h @@ -72,6 +72,22 @@ typedef struct { * spare space in SRAM to place the translation table there and specify it as * the 'mmu_tt_addr' field in the 'soc_sram_info' structure. The 'mmu_tt_addr' * address must be 16K aligned. + * + * If an SoC has the "secure boot" fuse burned, it will enter FEL mode in + * non-secure state, so with the SCR.NS bit set. Since in this mode the + * secure/non-secure state restrictions are actually observed, we suffer + * from several restrictions: + * - No access to the SID information (both via memory mapped and "register"). + * - No access to secure SRAM (SRAM A2 on H3/A64/H5). + * - No access to the secure side of the GIC, so it can't be configured to + * be accessible from non-secure world. + * - No RMR trigger on ARMv8 cores to bring the core into AArch64. + * However it has been found out that a simple "smc" call will immediately + * return from monitor mode, but with the NS bit cleared, so access to all + * secure peripherals is suddenly possible. + * The 'needs_smc_workaround_if_zero_word_at_addr' field can be used to + * have a check for this condition (reading from restricted addresses + * typically returns zero) and then activate the SMC workaround if needed. */ typedef struct { uint32_t soc_id; /* ID of the SoC */ @@ -86,6 +102,8 @@ typedef struct { uint32_t sid_offset; /* offset for SID_KEY[0-3], "root key" */ uint32_t rvbar_reg; /* MMIO address of RVBARADDR0_L register */ bool sid_fix; /* Use SID workaround (read via register) */ + /* Use SMC workaround (enter secure mode) if can't read from this address */ + uint32_t needs_smc_workaround_if_zero_word_at_addr; sram_swap_buffers *swap_buffers; } soc_info_t;