Skip to content

Commit 7dae1f5

Browse files
committed
sunxi-fel: handle H616 secure-FEL handoff
On H616 with the secure boot fuse set, FEL starts in non-secure state. The older direct SMC workaround is not sufficient there because the SMC returns through monitor mode instead of directly leaving the BROM FEL command loop in secure SVC state. Add a secure-SVC return thunk for this case and keep the existing global startup workaround model. The thunk issues the SMC, switches from monitor mode to secure SVC with the banked SP/LR restored, restores the secure GIC view expected by the BROM, and returns to the FEL command loop. It uses the same SRAM swap-table convention as the SPL thunk to preserve the H616 BROM SRAM workspace while the uploaded code runs and returns to FEL. After the transition, the normal runtime probe sees secure state and suppresses repeat application in that sunxi-fel process, so normal SID reads and SPL execution use the existing code paths. Describe the SMC workaround method in SoC data and make every existing secure-FEL user select the direct-SMC path explicitly. H616 selects the secure-SVC thunk path and additionally gates the workaround on the secure boot status word at SID + 0xa0, so non-secure H616 boards do not enter the secure path just because the zero-word probe also reads as zero. Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
1 parent 4719b9f commit 7dae1f5

8 files changed

Lines changed: 341 additions & 31 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ SOC_INFO := soc_info.c soc_info.h
141141
FEL_LIB := fel_lib.c fel_lib.h
142142
SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h
143143

144-
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
144+
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h thunks/fel-to-secure-svc-return-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
145145
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LDFLAGS) -o $@ \
146146
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) -lfdt
147147

fel.c

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@ static uint32_t fel_to_spl_thunk[] = {
390390
#include "thunks/fel-to-spl-thunk.h"
391391
};
392392

393+
static uint32_t fel_to_secure_svc_return_thunk[] = {
394+
#include "thunks/fel-to-secure-svc-return-thunk.h"
395+
};
396+
393397
#define DRAM_BASE 0x40000000
394398
#define DRAM_SIZE 0x80000000
395399

@@ -596,36 +600,104 @@ void aw_set_sctlr(feldev_handle *dev, soc_info_t *soc_info,
596600
aw_write_arm_cp_reg(dev, soc_info, 15, 0, 1, 0, 0, sctlr);
597601
}
598602

599-
/*
600-
* Issue a "smc #0" instruction. This brings a SoC booted in "secure boot"
601-
* state from the default non-secure FEL into secure FEL.
602-
* This crashes on devices using "non-secure boot", as the BROM does not
603-
* provide a handler address in MVBAR. So we have a runtime check.
604-
*/
605-
void aw_apply_smc_workaround(feldev_handle *dev)
603+
static bool aw_fel_needs_smc_workaround(feldev_handle *dev)
606604
{
607605
soc_info_t *soc_info = dev->soc_info;
608606
uint32_t val;
607+
608+
if (soc_info->secure_boot_fuse_addr) {
609+
aw_fel_read(dev, soc_info->secure_boot_fuse_addr,
610+
&val, sizeof(val));
611+
if (!(le32toh(val) & soc_info->secure_boot_fuse_mask))
612+
return false;
613+
614+
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
615+
return true;
616+
}
617+
618+
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
619+
return false;
620+
aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr,
621+
&val, sizeof(val));
622+
623+
return le32toh(val) == 0;
624+
}
625+
626+
static void aw_fel_execute_secure_svc_return_thunk(feldev_handle *dev)
627+
{
628+
soc_info_t *soc_info = dev->soc_info;
629+
const sram_swap_buffers *swap_buffers = soc_info->swap_buffers;
630+
struct timespec req = { .tv_nsec = 250000000 }; /* 250ms */
609631
uint32_t arm_code[] = {
610-
htole32(0xe1600070), /* smc #0 */
611-
htole32(0xe12fff1e), /* bx lr */
632+
htole32(0xe12fff1e), /* bx lr */
612633
};
634+
uint32_t *thunk_buf;
635+
size_t thunk_size;
636+
size_t i;
613637

614-
/* Return if the SoC does not need this workaround */
615-
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
616-
return;
638+
for (i = 0; swap_buffers[i].size; i++)
639+
;
617640

618-
/* This has less overhead than fel_readl_n() and may be good enough */
619-
aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr,
620-
&val, sizeof(val));
641+
thunk_size = sizeof(fel_to_secure_svc_return_thunk) +
642+
sizeof(soc_info->spl_addr) +
643+
(i + 1) * sizeof(*swap_buffers);
621644

622-
/* Return if the workaround is not needed or has been already applied */
623-
if (val != 0)
645+
if (thunk_size > soc_info->thunk_size)
646+
pr_fatal("FEL thunk: bad size (need %zu, have %u)\n",
647+
thunk_size, soc_info->thunk_size);
648+
649+
thunk_buf = malloc(thunk_size);
650+
if (!thunk_buf)
651+
pr_fatal("FEL thunk: failed to allocate buffer\n");
652+
memcpy(thunk_buf, fel_to_secure_svc_return_thunk,
653+
sizeof(fel_to_secure_svc_return_thunk));
654+
memcpy(thunk_buf + ARRAY_SIZE(fel_to_secure_svc_return_thunk),
655+
&soc_info->spl_addr, sizeof(soc_info->spl_addr));
656+
memcpy(thunk_buf + ARRAY_SIZE(fel_to_secure_svc_return_thunk) + 1,
657+
swap_buffers, (i + 1) * sizeof(*swap_buffers));
658+
659+
for (i = 0; i < thunk_size / sizeof(uint32_t); i++)
660+
thunk_buf[i] = htole32(thunk_buf[i]);
661+
662+
aw_fel_write(dev, arm_code, soc_info->spl_addr, sizeof(arm_code));
663+
aw_fel_write(dev, thunk_buf, soc_info->thunk_addr, thunk_size);
664+
aw_fel_execute(dev, soc_info->thunk_addr);
665+
free(thunk_buf);
666+
667+
/* TODO: Try to find and fix the bug, which needs this workaround */
668+
nanosleep(&req, NULL);
669+
}
670+
671+
/*
672+
* Apply the "smc #0" workaround. This moves a secure-boot FEL session from
673+
* the default non-secure state into secure state.
674+
* This crashes on devices using "non-secure boot", as the BROM does not
675+
* provide a handler address in MVBAR. So we have a runtime check.
676+
* Some newer SoCs need to perform the SMC and return to FEL via a thunk,
677+
* which handles the monitor-to-SVC transition details.
678+
*/
679+
static void aw_apply_smc_workaround(feldev_handle *dev)
680+
{
681+
soc_info_t *soc_info = dev->soc_info;
682+
683+
/* Return if the workaround is not needed */
684+
if (!aw_fel_needs_smc_workaround(dev))
624685
return;
625686

626-
pr_info("Applying SMC workaround... ");
627-
aw_fel_write(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code));
628-
aw_fel_execute(dev, soc_info->scratch_addr);
687+
if (soc_info->smc_workaround == SMC_WORKAROUND_SECURE_SVC_THUNK) {
688+
pr_info("Applying SMC workaround via secure-SVC return thunk... ");
689+
aw_fel_execute_secure_svc_return_thunk(dev);
690+
} else {
691+
uint32_t arm_code[] = {
692+
htole32(0xe1600070), /* smc #0 */
693+
htole32(0xe12fff1e), /* bx lr */
694+
};
695+
696+
pr_info("Applying SMC workaround... ");
697+
aw_fel_write(dev, arm_code, soc_info->scratch_addr,
698+
sizeof(arm_code));
699+
aw_fel_execute(dev, soc_info->scratch_addr);
700+
}
629701
pr_info(" done.\n");
630702
}
631703

@@ -1537,7 +1609,7 @@ int main(int argc, char **argv)
15371609
*/
15381610
handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID);
15391611

1540-
/* Some SoCs need the SMC workaround to enter the secure boot mode */
1612+
/* Some SoCs need the SMC workaround to enter secure state */
15411613
aw_apply_smc_workaround(handle);
15421614

15431615
/* Handle command-style arguments, in order of appearance */

soc_info.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ soc_info_t soc_info_table[] = {
397397
.rvbar_reg = 0x017000A0,
398398
/* Check L.NOP in the OpenRISC reset vector */
399399
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
400+
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
400401
.watchdog = &wd_h3_compat,
401402
},{
402403
.soc_id = 0x1639, /* Allwinner A80 */
@@ -445,6 +446,7 @@ soc_info_t soc_info_table[] = {
445446
.sid_sections = h3_sid_maps,
446447
/* Check L.NOP in the OpenRISC reset vector */
447448
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
449+
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
448450
.watchdog = &wd_h3_compat,
449451
},{
450452
.soc_id = 0x1681, /* Allwinner V3s */
@@ -483,6 +485,7 @@ soc_info_t soc_info_table[] = {
483485
.rvbar_reg = 0x017000A0,
484486
/* Check L.NOP in the OpenRISC reset vector */
485487
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
488+
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
486489
.watchdog = &wd_h3_compat,
487490
},{
488491
.soc_id = 0x1701, /* Allwinner R40 */
@@ -522,6 +525,7 @@ soc_info_t soc_info_table[] = {
522525
.rvbar_reg = 0x09010040,
523526
/* Check L.NOP in the OpenRISC reset vector */
524527
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
528+
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
525529
.watchdog = &wd_h6_compat,
526530
},{
527531
.soc_id = 0x1816, /* Allwinner V536 */
@@ -561,6 +565,10 @@ soc_info_t soc_info_table[] = {
561565
.rvbar_reg = 0x09010040,
562566
.rvbar_reg_alt= 0x08100040,
563567
.ver_reg = 0x03000024,
568+
.needs_smc_workaround_if_zero_word_at_addr = 0x03006240,
569+
.secure_boot_fuse_addr = 0x030060a0,
570+
.secure_boot_fuse_mask = 0xf,
571+
.smc_workaround = SMC_WORKAROUND_SECURE_SVC_THUNK,
564572
.watchdog = &wd_h6_compat,
565573
},{
566574
.soc_id = 0x1851, /* Allwinner R329 */
@@ -641,6 +649,7 @@ soc_info_t soc_info_table[] = {
641649
.sid_sections = generic_2k_sid_maps,
642650
.rvbar_reg = 0x08100040,
643651
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
652+
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
644653
.watchdog = &wd_h6_compat,
645654
},{
646655
.swap_buffers = NULL /* End of the table */

soc_info.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,24 @@ typedef struct {
107107
* - No access to the secure side of the GIC, so it can't be configured to
108108
* be accessible from non-secure world.
109109
* - No RMR trigger on ARMv8 cores to bring the core into AArch64.
110-
* However it has been found out that a simple "smc" call will immediately
111-
* return from monitor mode, but with the NS bit cleared, so access to all
112-
* secure peripherals is suddenly possible.
110+
* On older SoCs, a simple "smc" call returns with the NS bit cleared,
111+
* so access to all secure peripherals is suddenly possible. Newer SoCs
112+
* may need a secure-SVC thunk to handle the monitor-to-SVC transition
113+
* after the SMC call before returning to FEL.
113114
* The 'needs_smc_workaround_if_zero_word_at_addr' field can be used to
114115
* have a check for this condition (reading from restricted addresses
115116
* typically returns zero) and then activate the SMC workaround if needed.
117+
* The 'secure_boot_fuse_addr' and 'secure_boot_fuse_mask' fields can be used
118+
* when the SoC has an explicit readable secure boot status word. If both
119+
* checks are configured, then both conditions must match.
120+
* The 'smc_workaround' field selects how to apply the workaround once the
121+
* runtime checks say that it is needed.
116122
*/
123+
typedef enum {
124+
SMC_WORKAROUND_DIRECT_SMC,
125+
SMC_WORKAROUND_SECURE_SVC_THUNK,
126+
} smc_workaround_t;
127+
117128
typedef struct {
118129
uint32_t soc_id; /* ID of the SoC */
119130
const char *name; /* human-readable SoC name string */
@@ -135,6 +146,11 @@ typedef struct {
135146
bool icache_fix;
136147
/* Use SMC workaround (enter secure mode) if can't read from this address */
137148
uint32_t needs_smc_workaround_if_zero_word_at_addr;
149+
/* Use SMC workaround if any of these secure boot fuse bits are set */
150+
uint32_t secure_boot_fuse_addr;
151+
uint32_t secure_boot_fuse_mask;
152+
/* How to apply the SMC workaround */
153+
smc_workaround_t smc_workaround;
138154
uint32_t sram_size; /* Usable contiguous SRAM at spl_addr */
139155
sram_swap_buffers *swap_buffers;
140156
} soc_info_t;

thunks/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#
44

55
SPL_THUNK := fel-to-spl-thunk.h
6+
SPL_THUNK += fel-to-secure-svc-return-thunk.h
67
THUNKS := clrsetbits.h
78
THUNKS += memcpy.h
89
THUNKS += readl_writel.h
@@ -24,9 +25,9 @@ OBJDUMP := $(CROSS_COMPILE)objdump
2425

2526
AWK_O_TO_H := LC_ALL=C awk -f objdump_to_h.awk
2627

27-
# The SPL thunk requires a different output format. The "style" variable for
28+
# These thunks require the old output format. The "style" variable for
2829
# awk controls this, and causes the htole32() conversion to be omitted.
29-
fel-to-spl-thunk.h: fel-to-spl-thunk.S FORCE
30+
$(SPL_THUNK): %.h: %.S FORCE
3031
$(AS) -o $(subst .S,.o,$<) -march=armv5te $<
3132
$(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) -v style=old > $@
3233

thunks/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ usually via `sunxi-fel`.
88

99
Normally you don't need to change or (re)build anything within this folder.
1010
Currently our main build process (via the parent directory's _Makefile_)
11-
only includes `fel-to-spl-thunk.h` directly. Other _.h_ files are provided
12-
**just for reference**. The main purpose of this folder is simply keeping
13-
track of _.S_ sources, to help with possible future maintenance of the
14-
various code snippets.
11+
includes `fel-to-spl-thunk.h` and `fel-to-secure-svc-return-thunk.h` directly.
12+
Other _.h_ files are provided **just for reference**. The main purpose of this
13+
folder is simply keeping track of _.S_ sources, to help with possible future
14+
maintenance of the various code snippets.
1515

1616
Please note that any files lacking explicit license information are intended
1717
to be covered by the project's [overall license](../LICENSE.md) (GPLv2).

0 commit comments

Comments
 (0)