Skip to content

Commit ab40817

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 old one-shot SMC workaround is not sufficient there because the SMC returns through monitor mode, and returning to the BROM FEL command loop is not reliable enough to make the transition global. Add secure-SVC thunk variants for the paths that need secure state. SID reads use a return thunk so protected SID words can be displayed, while SPL execution uses a handoff thunk so U-Boot can switch the ARMv8 core to AArch64. 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 61e5766 commit ab40817

12 files changed

Lines changed: 579 additions & 57 deletions

fel.c

Lines changed: 91 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,14 @@ 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_spl_thunk[] = {
362+
#include "thunks/fel-to-secure-svc-spl-thunk.h"
363+
};
364+
365+
static uint32_t fel_to_secure_svc_return_thunk[] = {
366+
#include "thunks/fel-to-secure-svc-return-thunk.h"
367+
};
368+
361369
#define DRAM_BASE 0x40000000
362370
#define DRAM_SIZE 0x80000000
363371

@@ -415,6 +423,8 @@ void fel_writel(feldev_handle *dev, uint32_t addr, uint32_t val)
415423
fel_writel_n(dev, addr, &val, 1);
416424
}
417425

426+
static void aw_apply_smc_workaround(feldev_handle *dev);
427+
418428
void aw_fel_print_sid(feldev_handle *dev, bool force_workaround)
419429
{
420430
uint32_t key[4];
@@ -426,6 +436,8 @@ void aw_fel_print_sid(feldev_handle *dev, bool force_workaround)
426436
return;
427437
}
428438

439+
aw_apply_smc_workaround(dev);
440+
429441
if (soc_info->sid_fix || force_workaround) {
430442
pr_info("Read SID key via registers, base = 0x%08X\n",
431443
soc_info->sid_base);
@@ -451,6 +463,8 @@ void aw_fel_dump_sid(feldev_handle *dev)
451463
return;
452464
}
453465

466+
aw_apply_smc_workaround(dev);
467+
454468
for (const sid_section *s = soc_info->sid_sections; s->name; s++) {
455469
uint32_t count = s->size_bits / 32;
456470

@@ -564,6 +578,29 @@ void aw_set_sctlr(feldev_handle *dev, soc_info_t *soc_info,
564578
aw_write_arm_cp_reg(dev, soc_info, 15, 0, 1, 0, 0, sctlr);
565579
}
566580

581+
static bool aw_fel_needs_smc_workaround(feldev_handle *dev)
582+
{
583+
soc_info_t *soc_info = dev->soc_info;
584+
uint32_t val;
585+
586+
if (soc_info->secure_boot_fuse_addr) {
587+
aw_fel_read(dev, soc_info->secure_boot_fuse_addr,
588+
&val, sizeof(val));
589+
if (!(le32toh(val) & soc_info->secure_boot_fuse_mask))
590+
return false;
591+
592+
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
593+
return true;
594+
}
595+
596+
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
597+
return false;
598+
aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr,
599+
&val, sizeof(val));
600+
601+
return le32toh(val) == 0;
602+
}
603+
567604
static void aw_fel_execute_raw_thunk(feldev_handle *dev,
568605
const uint32_t *thunk,
569606
size_t thunk_size)
@@ -622,36 +659,49 @@ static void aw_fel_execute_thunk(feldev_handle *dev,
622659
}
623660

624661
/*
625-
* Issue a "smc #0" instruction. This brings a SoC booted in "secure boot"
626-
* state from the default non-secure FEL into secure FEL.
662+
* Apply the "smc #0" workaround. This moves a secure-boot FEL session from
663+
* the default non-secure state into secure state.
627664
* This crashes on devices using "non-secure boot", as the BROM does not
628665
* provide a handler address in MVBAR. So we have a runtime check.
666+
* Some newer SoCs need to perform the SMC and return to FEL via a thunk,
667+
* which handles the monitor-to-SVC transition details.
629668
*/
630-
void aw_apply_smc_workaround(feldev_handle *dev)
669+
static void aw_apply_smc_workaround(feldev_handle *dev)
631670
{
632671
soc_info_t *soc_info = dev->soc_info;
633-
uint32_t val;
634-
uint32_t arm_code[] = {
635-
htole32(0xe1600070), /* smc #0 */
636-
htole32(0xe12fff1e), /* bx lr */
637-
};
672+
static bool applied;
638673

639-
/* Return if the SoC does not need this workaround */
640-
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
674+
/* Return if the workaround is not needed or has been already applied */
675+
if (applied || !aw_fel_needs_smc_workaround(dev))
641676
return;
642677

643-
/* This has less overhead than fel_readl_n() and may be good enough */
644-
aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr,
645-
&val, sizeof(val));
678+
if (soc_info->smc_workaround == SMC_WORKAROUND_SECURE_SVC_THUNK) {
679+
uint32_t arm_code[] = {
680+
htole32(0xe12fff1e), /* bx lr */
681+
};
646682

647-
/* Return if the workaround is not needed or has been already applied */
648-
if (val != 0)
649-
return;
683+
pr_info("Applying SMC workaround via secure-SVC return thunk... ");
684+
aw_fel_write(dev, arm_code, soc_info->spl_addr,
685+
sizeof(arm_code));
686+
aw_fel_execute_thunk(dev, fel_to_secure_svc_return_thunk,
687+
sizeof(fel_to_secure_svc_return_thunk),
688+
soc_info->spl_addr,
689+
soc_info->swap_buffers);
690+
pr_info(" done.\n");
691+
} else {
692+
uint32_t arm_code[] = {
693+
htole32(0xe1600070), /* smc #0 */
694+
htole32(0xe12fff1e), /* bx lr */
695+
};
650696

651-
pr_info("Applying SMC workaround... ");
652-
aw_fel_write(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code));
653-
aw_fel_execute(dev, soc_info->scratch_addr);
654-
pr_info(" done.\n");
697+
pr_info("Applying SMC workaround... ");
698+
aw_fel_write(dev, arm_code, soc_info->scratch_addr,
699+
sizeof(arm_code));
700+
aw_fel_execute(dev, soc_info->scratch_addr);
701+
pr_info(" done.\n");
702+
}
703+
704+
applied = true;
655705
}
656706

657707
/*
@@ -814,14 +864,17 @@ void aw_restore_and_enable_mmu(feldev_handle *dev,
814864
/* Minimum offset of the main U-Boot image within u-boot-sunxi-with-spl.bin. */
815865
#define SPL_MIN_OFFSET 0x8000
816866

817-
uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t len)
867+
static uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf,
868+
size_t len)
818869
{
819870
soc_info_t *soc_info = dev->soc_info;
820871
sram_swap_buffers *swap_buffers;
821872
char header_signature[9] = { 0 };
822873
size_t i;
823874
uint32_t sp, sp_irq;
824875
uint32_t spl_checksum, spl_len, spl_len_limit;
876+
const uint32_t *thunk = fel_to_spl_thunk;
877+
size_t thunk_code_size = sizeof(fel_to_spl_thunk);
825878
uint32_t *buf32 = (uint32_t *)buf;
826879
uint32_t cur_addr = soc_info->spl_addr;
827880
uint32_t *tt = NULL;
@@ -913,8 +966,15 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
913966
if (len > 0)
914967
aw_fel_write(dev, buf, cur_addr, len);
915968

969+
if (soc_info->smc_workaround == SMC_WORKAROUND_SECURE_SVC_THUNK &&
970+
aw_fel_needs_smc_workaround(dev)) {
971+
thunk = fel_to_secure_svc_spl_thunk;
972+
thunk_code_size = sizeof(fel_to_secure_svc_spl_thunk);
973+
pr_info("SPL: enabling secure-SVC transition workaround\n");
974+
}
975+
916976
pr_info("=> Executing the SPL...");
917-
aw_fel_execute_thunk(dev, fel_to_spl_thunk, sizeof(fel_to_spl_thunk),
977+
aw_fel_execute_thunk(dev, thunk, thunk_code_size,
918978
soc_info->spl_addr, swap_buffers);
919979
pr_info(" done.\n");
920980

@@ -1414,8 +1474,15 @@ int main(int argc, char **argv)
14141474
*/
14151475
handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID);
14161476

1417-
/* Some SoCs need the SMC workaround to enter the secure boot mode */
1418-
aw_apply_smc_workaround(handle);
1477+
/*
1478+
* Some SoCs need the SMC workaround to enter secure state. On H616,
1479+
* SMC returns through monitor mode, and the secure-SVC transition is
1480+
* only reliable when it is part of the operation that needs secure
1481+
* state. Keep the global workaround for older SoCs, and let the SID
1482+
* and SPL paths request the H616 thunk at their point of use.
1483+
*/
1484+
if (handle->soc_info->smc_workaround != SMC_WORKAROUND_SECURE_SVC_THUNK)
1485+
aw_apply_smc_workaround(handle);
14191486

14201487
/* Handle command-style arguments, in order of appearance */
14211488
while (argc > 1 ) {

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: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
# build "preprocessed" .h files for inclusion of ARM scratch code
33
#
44

5-
SPL_THUNK := fel-to-spl-thunk.h
5+
FEL_EXEC_THUNKS := fel-to-spl-thunk.h
6+
FEL_EXEC_THUNKS += fel-to-secure-svc-spl-thunk.h
7+
FEL_EXEC_THUNKS += fel-to-secure-svc-return-thunk.h
68
THUNKS := clrsetbits.h
79
THUNKS += memcpy.h
810
THUNKS += readl_writel.h
911
THUNKS += rmr-thunk.h
1012
THUNKS += sid_read_root.h
1113

12-
all: $(SPL_THUNK) $(THUNKS)
14+
all: $(FEL_EXEC_THUNKS) $(THUNKS)
1315
# clean up object files afterwards
1416
rm -f *.o
1517

@@ -24,9 +26,10 @@ OBJDUMP := $(CROSS_COMPILE)objdump
2426

2527
AWK_O_TO_H := LC_ALL=C awk -f objdump_to_h.awk
2628

27-
# The SPL thunk requires a different output format. The "style" variable for
28-
# awk controls this, and causes the htole32() conversion to be omitted.
29-
fel-to-spl-thunk.h: fel-to-spl-thunk.S FORCE
29+
# These thunks are copied into a uint32_t buffer and byte-swapped by fel.c.
30+
# The "style" variable for awk controls this, and causes the htole32()
31+
# conversion to be omitted.
32+
$(FEL_EXEC_THUNKS): %.h: %.S FORCE
3033
$(AS) -o $(subst .S,.o,$<) -march=armv5te $<
3134
$(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) -v style=old > $@
3235

thunks/README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ can be transferred to a suitable target device and then executed 'remotely',
77
usually via `sunxi-fel`.
88

99
Normally you don't need to change or (re)build anything within this folder.
10-
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.
10+
Generated _.h_ files are included by the main build when referenced from
11+
`fel.c`. The main purpose of this folder is keeping track of _.S_ sources,
12+
to help with possible future maintenance of the various code snippets.
1513

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

thunks/fel-secure-svc.inc.S

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
SAVED_SP .req r10
2+
SAVED_LR .req r11
3+
4+
.set SUNXI_GICC_BASE, 0x03022000
5+
.set SUNXI_GICD_BASE, 0x03021000
6+
7+
.macro switch_to_secure_svc
8+
sync_before_branch
9+
mov SAVED_SP, sp
10+
mov SAVED_LR, lr
11+
.word 0xe1600070 /* smc #0 */
12+
mov TMP1, #0
13+
mcr p15, 0, TMP1, c1, c1, 0 /* SCR = secure */
14+
mcr p15, 0, TMP1, c12, c0, 1 /* MVBAR = 0 */
15+
bl setup_secure_gic
16+
17+
.word 0xe123f30a /* msr SP_svc, SAVED_SP */
18+
.word 0xe122f30b /* msr LR_svc, SAVED_LR */
19+
.word 0xf57ff06f /* isb */
20+
mrs TMP1, cpsr
21+
bic TMP1, TMP1, #0x1f
22+
orr TMP1, TMP1, #0x13
23+
msr cpsr_fc, TMP1 /* switch to secure-SVC mode */
24+
.endm
25+
26+
.macro emit_secure_svc_helpers
27+
setup_secure_gic:
28+
/* Set the secure GIC view expected by secure-SVC FEL execution. */
29+
ldr TMP1, gicc_ctlr
30+
mov TMP2, #0xf
31+
str TMP2, [TMP1]
32+
mov TMP2, #0xf8
33+
str TMP2, [TMP1, #4]
34+
ldr TMP1, gicd_ctlr
35+
mov TMP2, #3
36+
str TMP2, [TMP1]
37+
bx lr
38+
39+
gicc_ctlr:
40+
.word SUNXI_GICC_BASE
41+
gicd_ctlr:
42+
.word SUNXI_GICD_BASE
43+
.endm

0 commit comments

Comments
 (0)