Skip to content

Commit af8d769

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. 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 78463e4 commit af8d769

8 files changed

Lines changed: 313 additions & 22 deletions

File tree

Makefile

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

162-
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
162+
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)
163163
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LIBFDT_CFLAGS) $(LDFLAGS) -o $@ \
164164
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) $(LIBFDT_LIBS)
165165

fel.c

Lines changed: 81 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_return_thunk[] = {
362+
#include "thunks/fel-to-secure-svc-return-thunk.h"
363+
};
364+
361365
#define DRAM_BASE 0x40000000
362366
#define DRAM_SIZE 0x80000000
363367

@@ -569,38 +573,99 @@ 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_addr) {
577+
aw_fel_read(dev, soc_info->secure_boot_fuse_addr,
578+
&val, sizeof(val));
579+
if (!(le32toh(val) & soc_info->secure_boot_fuse_mask))
580+
return false;
581+
582+
if (!soc_info->needs_smc_workaround_if_zero_word_at_addr)
583+
return true;
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));
579590

580-
return val == 0;
591+
return le32toh(val) == 0;
592+
}
593+
594+
static void aw_fel_execute_secure_svc_return_thunk(feldev_handle *dev)
595+
{
596+
soc_info_t *soc_info = dev->soc_info;
597+
const sram_swap_buffers *swap_buffers = soc_info->swap_buffers;
598+
struct timespec req = { .tv_nsec = 250000000 }; /* 250ms */
599+
uint32_t arm_code[] = {
600+
htole32(0xe12fff1e), /* bx lr */
601+
};
602+
uint32_t *thunk_buf;
603+
size_t thunk_size;
604+
size_t i;
605+
606+
for (i = 0; swap_buffers[i].size; i++)
607+
;
608+
609+
thunk_size = sizeof(fel_to_secure_svc_return_thunk) +
610+
sizeof(soc_info->spl_addr) +
611+
(i + 1) * sizeof(*swap_buffers);
612+
613+
if (thunk_size > soc_info->thunk_size)
614+
pr_fatal("FEL thunk: bad size (need %zu, have %u)\n",
615+
thunk_size, soc_info->thunk_size);
616+
617+
thunk_buf = malloc(thunk_size);
618+
if (!thunk_buf)
619+
pr_fatal("FEL thunk: failed to allocate buffer\n");
620+
memcpy(thunk_buf, fel_to_secure_svc_return_thunk,
621+
sizeof(fel_to_secure_svc_return_thunk));
622+
memcpy(thunk_buf + ARRAY_SIZE(fel_to_secure_svc_return_thunk),
623+
&soc_info->spl_addr, sizeof(soc_info->spl_addr));
624+
memcpy(thunk_buf + ARRAY_SIZE(fel_to_secure_svc_return_thunk) + 1,
625+
swap_buffers, (i + 1) * sizeof(*swap_buffers));
626+
627+
for (i = 0; i < thunk_size / sizeof(uint32_t); i++)
628+
thunk_buf[i] = htole32(thunk_buf[i]);
629+
630+
aw_fel_write(dev, arm_code, soc_info->spl_addr, sizeof(arm_code));
631+
aw_fel_write(dev, thunk_buf, soc_info->thunk_addr, thunk_size);
632+
aw_fel_execute(dev, soc_info->thunk_addr);
633+
free(thunk_buf);
634+
635+
/* TODO: Try to find and fix the bug, which needs this workaround */
636+
nanosleep(&req, NULL);
581637
}
582638

583639
/*
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.
640+
* Apply the "smc #0" workaround. This moves a secure-boot FEL session from
641+
* the default non-secure state into secure state.
586642
* This crashes on devices using "non-secure boot", as the BROM does not
587643
* provide a handler address in MVBAR. So we have a runtime check.
644+
* Some newer SoCs need to perform the SMC and return to FEL via a thunk,
645+
* which handles the monitor-to-SVC transition details.
588646
*/
589647
static void aw_apply_smc_workaround(feldev_handle *dev)
590648
{
591649
soc_info_t *soc_info = dev->soc_info;
592-
uint32_t arm_code[] = {
593-
htole32(0xe1600070), /* smc #0 */
594-
htole32(0xe12fff1e), /* bx lr */
595-
};
596650

597-
/* Return if the workaround is not needed or has been already applied */
651+
/* Return if the workaround is not needed */
598652
if (!aw_fel_needs_smc_workaround(dev))
599653
return;
600654

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);
655+
if (soc_info->smc_workaround == SMC_WORKAROUND_SECURE_SVC_THUNK) {
656+
pr_info("Applying SMC workaround via secure-SVC return thunk... ");
657+
aw_fel_execute_secure_svc_return_thunk(dev);
658+
} else {
659+
uint32_t arm_code[] = {
660+
htole32(0xe1600070), /* smc #0 */
661+
htole32(0xe12fff1e), /* bx lr */
662+
};
663+
664+
pr_info("Applying SMC workaround... ");
665+
aw_fel_write(dev, arm_code, soc_info->scratch_addr,
666+
sizeof(arm_code));
667+
aw_fel_execute(dev, soc_info->scratch_addr);
668+
}
604669
pr_info(" done.\n");
605670
}
606671

@@ -1388,7 +1453,7 @@ int main(int argc, char **argv)
13881453
*/
13891454
handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID);
13901455

1391-
/* Some SoCs need the SMC workaround to enter the secure boot mode */
1456+
/* Some SoCs need the SMC workaround to enter secure state */
13921457
aw_apply_smc_workaround(handle);
13931458

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

soc_info.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,10 @@ soc_info_t soc_info_table[] = {
565565
.rvbar_reg = 0x09010040,
566566
.rvbar_reg_alt= 0x08100040,
567567
.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,
568572
.watchdog = &wd_h6_compat,
569573
},{
570574
.soc_id = 0x1851, /* Allwinner R329 */

soc_info.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,21 @@ 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_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.
115120
* The 'smc_workaround' field selects how to apply the workaround once the
116121
* runtime checks say that it is needed.
117122
*/
118123
typedef enum {
119124
SMC_WORKAROUND_DIRECT_SMC,
125+
SMC_WORKAROUND_SECURE_SVC_THUNK,
120126
} smc_workaround_t;
121127

122128
typedef struct {
@@ -140,6 +146,9 @@ typedef struct {
140146
bool icache_fix;
141147
/* Use SMC workaround (enter secure mode) if can't read from this address */
142148
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;
143152
/* How to apply the SMC workaround */
144153
smc_workaround_t smc_workaround;
145154
uint32_t sram_size; /* Usable contiguous SRAM at spl_addr */

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-return-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-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).
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/* H616 secure-FEL monitor-to-SVC return thunk. */
2+
3+
.arm
4+
5+
BUF1 .req r0
6+
BUF2 .req r1
7+
TMP1 .req r2
8+
TMP2 .req r3
9+
SWAPTBL .req r4
10+
BUFSIZE .req r6
11+
ENTRY_ADDR .req r8
12+
SAVED_SP .req r10
13+
SAVED_LR .req r11
14+
15+
.set SUNXI_GICC_BASE, 0x03022000
16+
.set SUNXI_GICD_BASE, 0x03021000
17+
18+
entry_point:
19+
b setup_stack
20+
21+
stack_begin:
22+
.space 32, 0xff
23+
stack_end:
24+
nop
25+
26+
/* A function, which walks the table and swaps all buffers */
27+
swap_all_buffers:
28+
adr SWAPTBL, appended_data + 4
29+
swap_next_buffer:
30+
ldr BUF1, [SWAPTBL], #4
31+
ldr BUF2, [SWAPTBL], #4
32+
ldr BUFSIZE, [SWAPTBL], #4
33+
cmp BUFSIZE, #0
34+
bxeq lr
35+
swap_next_word:
36+
ldr TMP1, [BUF1]
37+
ldr TMP2, [BUF2]
38+
subs BUFSIZE, BUFSIZE, #4
39+
str TMP1, [BUF2], #4
40+
str TMP2, [BUF1], #4
41+
bne swap_next_word
42+
b swap_next_buffer
43+
44+
setup_stack: /* Save the original SP, LR and CPSR to stack */
45+
ldr ENTRY_ADDR, appended_data
46+
adr BUF1, stack_end
47+
str sp, [BUF1, #-4]!
48+
mov sp, BUF1
49+
mrs TMP1, cpsr
50+
push {TMP1, lr}
51+
52+
/* Disable IRQ and FIQ */
53+
orr TMP1, #0xc0
54+
msr cpsr_c, TMP1
55+
56+
/* Check if the instructions or data cache is enabled */
57+
mrc p15, 0, TMP1, c1, c0, 0
58+
tst TMP1, #(1 << 2)
59+
tsteq TMP1, #(1 << 12)
60+
bne return_to_fel_noswap
61+
62+
bl swap_all_buffers
63+
bl call_entry
64+
b return_to_secure_fel
65+
66+
return_to_secure_fel:
67+
bl swap_all_buffers
68+
bl setup_secure_gic
69+
70+
return_to_fel_noswap:
71+
pop {TMP1, lr}
72+
msr cpsr_c, TMP1 /* Restore the original CPSR */
73+
ldr sp, [sp]
74+
bx lr
75+
76+
call_entry:
77+
mcr p15, 0, TMP1, c7, c10, 4 /* CP15DSB */
78+
mrc p15, 0, TMP1, c0, c0, 0 /* read MIDR */
79+
and TMP1, TMP1, #(0xf << 16) /* architecture */
80+
cmp TMP1, #(0x6 << 16) /* ARMv5TEJ */
81+
mcrgt p15, 0, TMP1, c7, c5, 4 /* CP15ISB, if > ARMv5TEJ */
82+
mov SAVED_SP, sp
83+
mov SAVED_LR, lr
84+
.word 0xe1600070 /* smc #0 */
85+
mov TMP1, #0
86+
mcr p15, 0, TMP1, c1, c1, 0 /* SCR = secure */
87+
mcr p15, 0, TMP1, c12, c0, 1 /* MVBAR = 0 */
88+
bl setup_secure_gic
89+
90+
.word 0xe123f30a /* msr SP_svc, SAVED_SP */
91+
.word 0xe122f30b /* msr LR_svc, SAVED_LR */
92+
.word 0xf57ff06f /* isb */
93+
mrs TMP1, cpsr
94+
bic TMP1, TMP1, #0x1f
95+
orr TMP1, TMP1, #0x13
96+
msr cpsr_fc, TMP1 /* switch to secure-SVC mode */
97+
bx ENTRY_ADDR
98+
99+
setup_secure_gic:
100+
/* Set the secure GIC view expected by secure-SVC FEL execution. */
101+
ldr TMP1, gicc_ctlr
102+
mov TMP2, #0xf
103+
str TMP2, [TMP1]
104+
mov TMP2, #0xf8
105+
str TMP2, [TMP1, #4]
106+
ldr TMP1, gicd_ctlr
107+
mov TMP2, #3
108+
str TMP2, [TMP1]
109+
bx lr
110+
111+
gicc_ctlr:
112+
.word SUNXI_GICC_BASE
113+
gicd_ctlr:
114+
.word SUNXI_GICD_BASE
115+
116+
appended_data:
117+
/*
118+
* The appended data uses the following format:
119+
*
120+
* struct {
121+
* uint32_t entry_addr;
122+
* sram_swap_buffers swaptbl[];
123+
* };
124+
*/

0 commit comments

Comments
 (0)