Skip to content

Commit 33cc609

Browse files
committed
sunxi-fel: handle H616/A133 secure-FEL handoff
On H616 and A133 with the secure boot fuse set, FEL starts in non-secure state. The older direct SMC workaround is not sufficient there because SMC enters monitor mode instead of directly leaving the BROM FEL command loop in secure SVC state. Add a secure-SVC SMC thunk for this case and keep the existing global startup workaround model. The thunk preserves the BROM SRAM workspace using the same swap-table convention as the SPL thunk, installs a temporary monitor-mode SMC handler by patching only the SMC vector word, then issues SMC. The temporary handler restores the original vector word, clears SCR.NS, clears MVBAR, restores the secure GIC view expected by the BROM, copies the saved SVC SP/LR into the secure bank, switches to secure SVC, and returns to the FEL command loop. 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. H616 and A133 select the secure-SVC SMC thunk path and gate the workaround on a non-zero secure boot status word at SID base + 0xa0. The zero-word runtime probe still has to match before the thunk is applied, so non-secure boards and already-transitioned secure-FEL sessions do not enter the secure path just because one of those checks matches. Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
1 parent 4ac3bf1 commit 33cc609

8 files changed

Lines changed: 388 additions & 24 deletions

File tree

Makefile

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

167-
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
167+
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h thunks/fel-to-secure-svc-smc-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
168168
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LIBFDT_CFLAGS) $(LDFLAGS) -o $@ \
169169
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) $(LIBFDT_LIBS)
170170

fel.c

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,10 @@ static uint32_t fel_to_spl_thunk[] = {
358358
#include "thunks/fel-to-spl-thunk.h"
359359
};
360360

361+
static uint32_t fel_to_secure_svc_smc_thunk[] = {
362+
#include "thunks/fel-to-secure-svc-smc-thunk.h"
363+
};
364+
361365
#define DRAM_BASE 0x40000000
362366
#define DRAM_SIZE 0x80000000
363367

@@ -569,38 +573,106 @@ static bool aw_fel_needs_smc_workaround(feldev_handle *dev)
569573
soc_info_t *soc_info = dev->soc_info;
570574
uint32_t val;
571575

572-
/* Return if the SoC does not need this workaround */
576+
if (soc_info->secure_boot_fuse_offset) {
577+
if (!soc_info->sid_base)
578+
return false;
579+
aw_fel_read(dev, soc_info->sid_base +
580+
soc_info->secure_boot_fuse_offset,
581+
&val, sizeof(val));
582+
if (!le32toh(val))
583+
return false;
584+
}
585+
573586
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
574587
return false;
575-
576-
/* This has less overhead than fel_readl_n() and may be good enough */
577588
aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr,
578-
&val, sizeof(val));
589+
&val, sizeof(val));
590+
591+
return le32toh(val) == 0;
592+
}
593+
594+
static void aw_fel_execute_secure_svc_smc_thunk(feldev_handle *dev)
595+
{
596+
soc_info_t *soc_info = dev->soc_info;
597+
const monitor_smc_handler *handler = soc_info->monitor_smc_handler;
598+
const sram_swap_buffers *swap_buffers = soc_info->swap_buffers;
599+
struct timespec req = { .tv_nsec = 250000000 }; /* 250ms */
600+
uint32_t arm_code[] = {
601+
htole32(0xe12fff1e), /* bx lr */
602+
};
603+
uint32_t *thunk_buf, *p;
604+
size_t thunk_size;
605+
size_t i;
606+
607+
if (!handler)
608+
pr_fatal("FEL thunk: missing monitor SMC handler info\n");
609+
610+
for (i = 0; swap_buffers[i].size; i++)
611+
;
612+
613+
thunk_size = sizeof(fel_to_secure_svc_smc_thunk) +
614+
4 * sizeof(uint32_t) +
615+
(i + 1) * sizeof(*swap_buffers);
616+
617+
if (thunk_size > soc_info->thunk_size)
618+
pr_fatal("FEL thunk: bad size (need %zu, have %u)\n",
619+
thunk_size, soc_info->thunk_size);
579620

580-
return val == 0;
621+
thunk_buf = malloc(thunk_size);
622+
if (!thunk_buf)
623+
pr_fatal("FEL thunk: failed to allocate buffer\n");
624+
memcpy(thunk_buf, fel_to_secure_svc_smc_thunk,
625+
sizeof(fel_to_secure_svc_smc_thunk));
626+
627+
p = thunk_buf + ARRAY_SIZE(fel_to_secure_svc_smc_thunk);
628+
*p++ = soc_info->spl_addr;
629+
*p++ = handler->vector_addr;
630+
*p++ = handler->gicc_base;
631+
*p++ = handler->gicd_base;
632+
memcpy(p, swap_buffers, (i + 1) * sizeof(*swap_buffers));
633+
634+
for (i = 0; i < thunk_size / sizeof(uint32_t); i++)
635+
thunk_buf[i] = htole32(thunk_buf[i]);
636+
637+
aw_fel_write(dev, arm_code, soc_info->spl_addr, sizeof(arm_code));
638+
aw_fel_write(dev, thunk_buf, soc_info->thunk_addr, thunk_size);
639+
aw_fel_execute(dev, soc_info->thunk_addr);
640+
free(thunk_buf);
641+
642+
/* TODO: Try to find and fix the bug, which needs this workaround */
643+
nanosleep(&req, NULL);
581644
}
582645

583646
/*
584-
* Issue a "smc #0" instruction. This brings a SoC booted in "secure boot"
585-
* state from the default non-secure FEL into secure FEL.
647+
* Apply the "smc #0" workaround. This moves a secure-boot FEL session from
648+
* the default non-secure state into secure state.
586649
* This crashes on devices using "non-secure boot", as the BROM does not
587650
* provide a handler address in MVBAR. So we have a runtime check.
651+
* Some newer SoCs need to perform the SMC and return to FEL via a thunk,
652+
* which handles the monitor-to-SVC transition details.
588653
*/
589654
static void aw_apply_smc_workaround(feldev_handle *dev)
590655
{
591656
soc_info_t *soc_info = dev->soc_info;
592-
uint32_t arm_code[] = {
593-
htole32(0xe1600070), /* smc #0 */
594-
htole32(0xe12fff1e), /* bx lr */
595-
};
596657

597-
/* Return if the workaround is not needed or has been already applied */
658+
/* Return if the workaround is not needed */
598659
if (!aw_fel_needs_smc_workaround(dev))
599660
return;
600661

601-
pr_info("Applying SMC workaround... ");
602-
aw_fel_write(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code));
603-
aw_fel_execute(dev, soc_info->scratch_addr);
662+
if (soc_info->smc_workaround == SMC_WORKAROUND_SECURE_SVC_SMC_THUNK) {
663+
pr_info("Applying SMC workaround via secure-SVC SMC thunk... ");
664+
aw_fel_execute_secure_svc_smc_thunk(dev);
665+
} else {
666+
uint32_t arm_code[] = {
667+
htole32(0xe1600070), /* smc #0 */
668+
htole32(0xe12fff1e), /* bx lr */
669+
};
670+
671+
pr_info("Applying SMC workaround... ");
672+
aw_fel_write(dev, arm_code, soc_info->scratch_addr,
673+
sizeof(arm_code));
674+
aw_fel_execute(dev, soc_info->scratch_addr);
675+
}
604676
pr_info(" done.\n");
605677
}
606678

@@ -1388,7 +1460,7 @@ int main(int argc, char **argv)
13881460
*/
13891461
handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID);
13901462

1391-
/* Some SoCs need the SMC workaround to enter the secure boot mode */
1463+
/* Some SoCs need the SMC workaround to enter secure state */
13921464
aw_apply_smc_workaround(handle);
13931465

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

soc_info.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ sram_swap_buffers a133_sram_swap_buffers[] = {
156156
{ .size = 0 } /* End of the table */
157157
};
158158

159+
static const monitor_smc_handler h616_monitor_smc_handler = {
160+
.vector_addr = 0x000300c0,
161+
.gicc_base = 0x03022000,
162+
.gicd_base = 0x03021000,
163+
};
164+
159165
/*
160166
* R329 has no SRAM A1, but a huge SRAM A2 at 0x100000. SPL and BROM uses
161167
* this SRAM A2's first part like how other SoCs use SRAM A1. The sp and
@@ -565,6 +571,10 @@ soc_info_t soc_info_table[] = {
565571
.rvbar_reg = 0x09010040,
566572
.rvbar_reg_alt= 0x08100040,
567573
.ver_reg = 0x03000024,
574+
.needs_smc_workaround_if_zero_word_at_addr = 0x03006240,
575+
.secure_boot_fuse_offset = 0xa0,
576+
.smc_workaround = SMC_WORKAROUND_SECURE_SVC_SMC_THUNK,
577+
.monitor_smc_handler = &h616_monitor_smc_handler,
568578
.watchdog = &wd_h6_compat,
569579
},{
570580
.soc_id = 0x1851, /* Allwinner R329 */
@@ -644,8 +654,10 @@ soc_info_t soc_info_table[] = {
644654
.sid_offset = 0x200,
645655
.sid_sections = generic_2k_sid_maps,
646656
.rvbar_reg = 0x08100040,
647-
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
648-
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
657+
.needs_smc_workaround_if_zero_word_at_addr = 0x03006240,
658+
.secure_boot_fuse_offset = 0xa0,
659+
.smc_workaround = SMC_WORKAROUND_SECURE_SVC_SMC_THUNK,
660+
.monitor_smc_handler = &h616_monitor_smc_handler,
649661
.watchdog = &wd_h6_compat,
650662
},{
651663
.swap_buffers = NULL /* End of the table */

soc_info.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,17 +108,30 @@ typedef struct {
108108
* be accessible from non-secure world.
109109
* - No RMR trigger on ARMv8 cores to bring the core into AArch64.
110110
* On older SoCs, a simple "smc" call returns with the NS bit cleared,
111-
* so access to all secure peripherals is suddenly possible.
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.
112114
* The 'needs_smc_workaround_if_zero_word_at_addr' field can be used to
113115
* have a check for this condition (reading from restricted addresses
114116
* typically returns zero) and then activate the SMC workaround if needed.
117+
* The 'secure_boot_fuse_offset' field can be used when the SoC has an
118+
* explicit readable secure boot status word at sid_base + offset. It is a
119+
* gate only; a separate runtime state probe is still required to avoid
120+
* applying the workaround on every invocation.
115121
* The 'smc_workaround' field selects how to apply the workaround once the
116122
* runtime checks say that it is needed.
117123
*/
118124
typedef enum {
119125
SMC_WORKAROUND_DIRECT_SMC,
126+
SMC_WORKAROUND_SECURE_SVC_SMC_THUNK,
120127
} smc_workaround_t;
121128

129+
typedef struct {
130+
uint32_t vector_addr;
131+
uint32_t gicc_base;
132+
uint32_t gicd_base;
133+
} monitor_smc_handler;
134+
122135
typedef struct {
123136
uint32_t soc_id; /* ID of the SoC */
124137
const char *name; /* human-readable SoC name string */
@@ -140,8 +153,11 @@ typedef struct {
140153
bool icache_fix;
141154
/* Use SMC workaround (enter secure mode) if can't read from this address */
142155
uint32_t needs_smc_workaround_if_zero_word_at_addr;
156+
/* Require non-zero sid_base + offset before applying SMC workaround */
157+
uint32_t secure_boot_fuse_offset;
143158
/* How to apply the SMC workaround */
144159
smc_workaround_t smc_workaround;
160+
const monitor_smc_handler *monitor_smc_handler;
145161
uint32_t sram_size; /* Usable contiguous SRAM at spl_addr */
146162
sram_swap_buffers *swap_buffers;
147163
} soc_info_t;

thunks/Makefile

Lines changed: 1 addition & 0 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-smc-thunk.h
67
THUNKS := clrsetbits.h
78
THUNKS += memcpy.h
89
THUNKS += readl_writel.h

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-smc-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)