Skip to content

Commit 4d91982

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 insufficient because SMC enters monitor mode instead of returning the BROM FEL command loop in secure SVC state. A133 also has its monitor vector table cleared by NBROM. 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. Before issuing SMC, it temporarily replaces the SMC vector instruction at MVBAR + 8 with "mov pc, lr". This returns to the instruction after SMC without restoring SPSR_mon, so execution continues in monitor mode. The thunk then restores the original vector instruction, clears SCR.NS and MVBAR, sets the secure GIC state 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 check matches. Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
1 parent 4ac3bf1 commit 4d91982

8 files changed

Lines changed: 341 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 secure_svc_smc_info *smc_info = soc_info->secure_svc_smc;
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 (!smc_info)
608+
pr_fatal("FEL thunk: missing secure-SVC SMC 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++ = smc_info->monitor_vector_addr;
630+
*p++ = smc_info->gicc_base;
631+
*p++ = smc_info->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 secure_svc_smc_info h616_a133_secure_svc_smc = {
160+
.monitor_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+
.secure_svc_smc = &h616_a133_secure_svc_smc,
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+
.secure_svc_smc = &h616_a133_secure_svc_smc,
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 monitor_vector_addr;
131+
uint32_t gicc_base;
132+
uint32_t gicd_base;
133+
} secure_svc_smc_info;
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 secure_svc_smc_info *secure_svc_smc;
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).
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/* secure-FEL thunk returning from SMC in monitor mode. */
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+
ORIG_VECTOR .req r5
11+
BUFSIZE .req r6
12+
ENTRY_ADDR .req r8
13+
MON_VECTOR .req r9
14+
SAVED_SP .req r10
15+
SAVED_LR .req r11
16+
17+
entry_point:
18+
b setup_stack
19+
20+
stack_begin:
21+
.space 32, 0xff
22+
stack_end:
23+
nop
24+
25+
/* Walk the table and swap all buffers. */
26+
swap_all_buffers:
27+
adr SWAPTBL, appended_data + 16
28+
swap_next_buffer:
29+
ldr BUF1, [SWAPTBL], #4
30+
ldr BUF2, [SWAPTBL], #4
31+
ldr BUFSIZE, [SWAPTBL], #4
32+
cmp BUFSIZE, #0
33+
bxeq lr
34+
swap_next_word:
35+
ldr TMP1, [BUF1]
36+
ldr TMP2, [BUF2]
37+
subs BUFSIZE, BUFSIZE, #4
38+
str TMP1, [BUF2], #4
39+
str TMP2, [BUF1], #4
40+
bne swap_next_word
41+
b swap_next_buffer
42+
43+
setup_stack:
44+
ldr ENTRY_ADDR, appended_data
45+
ldr MON_VECTOR, appended_data + 4
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+
69+
return_to_fel_noswap:
70+
pop {TMP1, lr}
71+
msr cpsr_c, TMP1
72+
ldr sp, [sp]
73+
bx lr
74+
75+
call_entry:
76+
/* Make the monitor SMC vector return without restoring SPSR_mon. */
77+
ldr ORIG_VECTOR, [MON_VECTOR, #8]
78+
ldr TMP1, monitor_return
79+
str TMP1, [MON_VECTOR, #8]
80+
81+
/* Save the non-secure SVC SP/LR for the secure bank below. */
82+
mcr p15, 0, TMP1, c7, c10, 4 /* CP15DSB */
83+
mrc p15, 0, TMP1, c0, c0, 0 /* read MIDR */
84+
and TMP1, TMP1, #(0xf << 16) /* architecture */
85+
cmp TMP1, #(0x6 << 16) /* ARMv5TEJ */
86+
mcrgt p15, 0, TMP1, c7, c5, 4 /* CP15ISB, if > ARMv5TEJ */
87+
mov SAVED_SP, sp
88+
mov SAVED_LR, lr
89+
.word 0xe1600070 /* smc #0 */
90+
91+
/* The monitor SMC vector returns here without changing mode. */
92+
str ORIG_VECTOR, [MON_VECTOR, #8]
93+
mov TMP1, #0
94+
mcr p15, 0, TMP1, c1, c1, 0 /* SCR = secure */
95+
mcr p15, 0, TMP1, c12, c0, 1 /* MVBAR = 0 */
96+
97+
ldr TMP1, appended_data + 8
98+
mov TMP2, #0xf
99+
str TMP2, [TMP1]
100+
mov TMP2, #0xf8
101+
str TMP2, [TMP1, #4]
102+
ldr TMP1, appended_data + 12
103+
mov TMP2, #3
104+
str TMP2, [TMP1]
105+
106+
.word 0xe123f30a /* msr SP_svc, SAVED_SP */
107+
.word 0xe122f30b /* msr LR_svc, SAVED_LR */
108+
.word 0xf57ff06f /* isb */
109+
mrs TMP1, cpsr
110+
bic TMP1, TMP1, #0x1f
111+
orr TMP1, TMP1, #0x13
112+
msr cpsr_fc, TMP1 /* switch to secure-SVC mode */
113+
bx ENTRY_ADDR
114+
115+
monitor_return:
116+
.word 0xe1a0f00e /* mov pc, lr */
117+
118+
appended_data:
119+
/*
120+
* The appended data uses the following format:
121+
*
122+
* struct {
123+
* uint32_t entry_addr;
124+
* uint32_t monitor_vector_addr;
125+
* uint32_t gicc_ctlr_addr;
126+
* uint32_t gicd_ctlr_addr;
127+
* sram_swap_buffers swaptbl[];
128+
* };
129+
*/

0 commit comments

Comments
 (0)