From 8c45b33e22c136204a61e2cb94196ff538c2b7be Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 28 Feb 2017 20:57:01 +0200 Subject: [PATCH 1/2] fel: SMC workaround to enter "secure boot" FEL mode on some SoCs 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. Those limitations make a board pretty useless for many applications. 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. Add all the necessary support code for doing a runtime check and activating this workaround. Affected SoCs need to have the "smc" workaround enabled in their soc_info struct. Signed-off-by: Andre Przywara ["sunxi-fel smc" command changed to automatic detection by Siarhei] Signed-off-by: Siarhei Siamashka --- fel.c | 36 ++++++++++++++++++++++++++++++++++++ soc_info.h | 18 ++++++++++++++++++ 2 files changed, 54 insertions(+) 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.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; From 275827ad7309fa989630604f073739d67d144886 Mon Sep 17 00:00:00 2001 From: Siarhei Siamashka Date: Tue, 28 Feb 2017 21:10:44 +0200 Subject: [PATCH 2/2] fel: Enable the SMC workaround for H3/H5/A64/H64 Use a hardwired L.NOP instruction from the OpenRISC reset vector as a way to check if the workaround is necessary. Because these L.NOP instructions are guaranteed to be there and are read-only, this is the most reliable non-invasive test. Reading SID would be less reliable because it is one-time programmable and theoretically may be set to zero on some boards. Signed-off-by: Siarhei Siamashka --- soc_info.c | 6 ++++++ 1 file changed, 6 insertions(+) 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",