Skip to content

Commit 61e5766

Browse files
committed
sunxi-fel: factor FEL thunk execution
Move the common SPL thunk body into a shared assembly include so that other thunk variants can reuse the same SRAM buffer swap, checksum validation and return-to-FEL handling. Also factor the host-side thunk upload and execute sequence into helper functions. This keeps the existing FEL-to-SPL thunk payload unchanged, while making the entry address and swap table handling reusable. Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
1 parent 7540cb2 commit 61e5766

3 files changed

Lines changed: 224 additions & 186 deletions

File tree

fel.c

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,63 @@ void aw_set_sctlr(feldev_handle *dev, soc_info_t *soc_info,
564564
aw_write_arm_cp_reg(dev, soc_info, 15, 0, 1, 0, 0, sctlr);
565565
}
566566

567+
static void aw_fel_execute_raw_thunk(feldev_handle *dev,
568+
const uint32_t *thunk,
569+
size_t thunk_size)
570+
{
571+
soc_info_t *soc_info = dev->soc_info;
572+
struct timespec req = { .tv_nsec = 250000000 }; /* 250ms */
573+
uint32_t *thunk_buf;
574+
size_t i;
575+
576+
if (thunk_size > soc_info->thunk_size)
577+
pr_fatal("FEL thunk: bad size (need %zu, have %u)\n",
578+
thunk_size, soc_info->thunk_size);
579+
580+
thunk_buf = malloc(thunk_size);
581+
if (!thunk_buf)
582+
pr_fatal("FEL thunk: failed to allocate buffer\n");
583+
memcpy(thunk_buf, thunk, thunk_size);
584+
585+
for (i = 0; i < thunk_size / sizeof(uint32_t); i++)
586+
thunk_buf[i] = htole32(thunk_buf[i]);
587+
588+
aw_fel_write(dev, thunk_buf, soc_info->thunk_addr, thunk_size);
589+
aw_fel_execute(dev, soc_info->thunk_addr);
590+
free(thunk_buf);
591+
592+
/* TODO: Try to find and fix the bug, which needs this workaround */
593+
nanosleep(&req, NULL);
594+
}
595+
596+
static void aw_fel_execute_thunk(feldev_handle *dev,
597+
const uint32_t *thunk,
598+
size_t thunk_code_size, uint32_t entry_addr,
599+
const sram_swap_buffers *swap_buffers)
600+
{
601+
size_t thunk_size;
602+
uint32_t *thunk_buf;
603+
size_t i;
604+
605+
for (i = 0; swap_buffers[i].size; i++)
606+
;
607+
608+
thunk_size = thunk_code_size + sizeof(entry_addr) +
609+
(i + 1) * sizeof(*swap_buffers);
610+
611+
thunk_buf = malloc(thunk_size);
612+
if (!thunk_buf)
613+
pr_fatal("FEL thunk: failed to allocate buffer\n");
614+
memcpy(thunk_buf, thunk, thunk_code_size);
615+
memcpy(thunk_buf + thunk_code_size / sizeof(uint32_t), &entry_addr,
616+
sizeof(entry_addr));
617+
memcpy(thunk_buf + thunk_code_size / sizeof(uint32_t) + 1,
618+
swap_buffers, (i + 1) * sizeof(*swap_buffers));
619+
620+
aw_fel_execute_raw_thunk(dev, thunk_buf, thunk_size);
621+
free(thunk_buf);
622+
}
623+
567624
/*
568625
* Issue a "smc #0" instruction. This brings a SoC booted in "secure boot"
569626
* state from the default non-secure FEL into secure FEL.
@@ -762,8 +819,7 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
762819
soc_info_t *soc_info = dev->soc_info;
763820
sram_swap_buffers *swap_buffers;
764821
char header_signature[9] = { 0 };
765-
size_t i, thunk_size;
766-
uint32_t *thunk_buf;
822+
size_t i;
767823
uint32_t sp, sp_irq;
768824
uint32_t spl_checksum, spl_len, spl_len_limit;
769825
uint32_t *buf32 = (uint32_t *)buf;
@@ -857,34 +913,11 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
857913
if (len > 0)
858914
aw_fel_write(dev, buf, cur_addr, len);
859915

860-
thunk_size = sizeof(fel_to_spl_thunk) + sizeof(soc_info->spl_addr) +
861-
(i + 1) * sizeof(*swap_buffers);
862-
863-
if (thunk_size > soc_info->thunk_size)
864-
pr_fatal("SPL: bad thunk size (need %d, have %d)\n",
865-
(int)sizeof(fel_to_spl_thunk), soc_info->thunk_size);
866-
867-
thunk_buf = malloc(thunk_size);
868-
memcpy(thunk_buf, fel_to_spl_thunk, sizeof(fel_to_spl_thunk));
869-
memcpy(thunk_buf + sizeof(fel_to_spl_thunk) / sizeof(uint32_t),
870-
&soc_info->spl_addr, sizeof(soc_info->spl_addr));
871-
memcpy(thunk_buf + sizeof(fel_to_spl_thunk) / sizeof(uint32_t) + 1,
872-
swap_buffers, (i + 1) * sizeof(*swap_buffers));
873-
874-
for (i = 0; i < thunk_size / sizeof(uint32_t); i++)
875-
thunk_buf[i] = htole32(thunk_buf[i]);
876-
877916
pr_info("=> Executing the SPL...");
878-
aw_fel_write(dev, thunk_buf, soc_info->thunk_addr, thunk_size);
879-
aw_fel_execute(dev, soc_info->thunk_addr);
917+
aw_fel_execute_thunk(dev, fel_to_spl_thunk, sizeof(fel_to_spl_thunk),
918+
soc_info->spl_addr, swap_buffers);
880919
pr_info(" done.\n");
881920

882-
free(thunk_buf);
883-
884-
/* TODO: Try to find and fix the bug, which needs this workaround */
885-
struct timespec req = { .tv_nsec = 250000000 }; /* 250ms */
886-
nanosleep(&req, NULL);
887-
888921
/* Read back the result and check if everything was fine */
889922
aw_fel_read(dev, soc_info->spl_addr + 4, header_signature, 8);
890923
if (strcmp(header_signature, "eGON.FEL") != 0)

thunks/fel-spl-thunk-common.S

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright © 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a
5+
* copy of this software and associated documentation files (the "Software"),
6+
* to deal in the Software without restriction, including without limitation
7+
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8+
* and/or sell copies of the Software, and to permit persons to whom the
9+
* Software is furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice (including the next
12+
* paragraph) shall be included in all copies or substantial portions of the
13+
* Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18+
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21+
* DEALINGS IN THE SOFTWARE.
22+
*/
23+
24+
.arm
25+
26+
BUF1 .req r0
27+
BUF2 .req r1
28+
TMP1 .req r2
29+
TMP2 .req r3
30+
SWAPTBL .req r4
31+
FULLSIZE .req r5
32+
BUFSIZE .req r6
33+
CHECKSUM .req r7
34+
ENTRY_ADDR .req r8
35+
36+
entry_point:
37+
b setup_stack
38+
39+
stack_begin:
40+
.space 32, 0xff
41+
stack_end:
42+
nop
43+
44+
/* A function, which walks the table and swaps all buffers */
45+
swap_all_buffers:
46+
adr SWAPTBL, appended_data + 4
47+
swap_next_buffer:
48+
ldr BUF1, [SWAPTBL], #4
49+
ldr BUF2, [SWAPTBL], #4
50+
ldr BUFSIZE, [SWAPTBL], #4
51+
cmp BUFSIZE, #0
52+
bxeq lr
53+
swap_next_word:
54+
ldr TMP1, [BUF1]
55+
ldr TMP2, [BUF2]
56+
subs BUFSIZE, BUFSIZE, #4
57+
str TMP1, [BUF2], #4
58+
str TMP2, [BUF1], #4
59+
bne swap_next_word
60+
b swap_next_buffer
61+
62+
setup_stack: /* Save the original SP, LR and CPSR to stack */
63+
ldr ENTRY_ADDR, appended_data
64+
adr BUF1, stack_end
65+
str sp, [BUF1, #-4]!
66+
mov sp, BUF1
67+
mrs TMP1, cpsr
68+
push {TMP1, lr}
69+
70+
/* Disable IRQ and FIQ */
71+
orr TMP1, #0xc0
72+
msr cpsr_c, TMP1
73+
74+
/* Check if the instructions or data cache is enabled */
75+
mrc p15, 0, TMP1, c1, c0, 0
76+
tst TMP1, #(1 << 2)
77+
tsteq TMP1, #(1 << 12)
78+
bne cache_is_unsupported
79+
80+
bl swap_all_buffers
81+
82+
verify_checksum:
83+
ldr CHECKSUM, checksum_seed
84+
mov BUF1, ENTRY_ADDR
85+
ldr FULLSIZE, [BUF1, #16]
86+
check_next_word:
87+
ldr TMP1, [BUF1], #4
88+
subs FULLSIZE, FULLSIZE, #4
89+
add CHECKSUM, CHECKSUM, TMP1
90+
bne check_next_word
91+
92+
ldr TMP1, [ENTRY_ADDR, #12]
93+
subs CHECKSUM, CHECKSUM, TMP1, lsl #1
94+
bne checksum_is_bad
95+
96+
/* Change 'eGON.BT0' -> 'eGON.FEL' */
97+
ldr TMP1, egon_fel_str
98+
str TMP1, [ENTRY_ADDR, #8]
99+
100+
/*
101+
* Call the SPL code, but before that make sure the CPU sees the
102+
* recently uploaded code. This requires a DSB and ISB.
103+
* The "dsb" and "isb" *instructions* are not available in ARMv5TE,
104+
* but at least for DSB we can use the CP15 register encoding. This
105+
* works for ARMv7 and v8 as well, because we have checked our SCTLR
106+
* before (in fel.c), so we know that CP15BEN is set.
107+
* The ARM926 core does not implement ISB, instead the TRM recommends
108+
* just a branch to achieve the same "flush the pipeline" effect.
109+
* As just this is not sufficient for later cores, check the MIDR
110+
* register, and do the DSB only for ARMv6 or later.
111+
* The input register for the CP15 instruction is ignored.
112+
*/
113+
mcr p15, 0, TMP1, c7, c10, 4 /* CP15DSB */
114+
mrc p15, 0, TMP1, c0, c0, 0 /* read MIDR */
115+
and TMP1, TMP1, #(0xf << 16) /* architecture */
116+
cmp TMP1, #(0x6 << 16) /* ARMv5TEJ */
117+
mcrgt p15, 0, TMP1, c7, c5, 4 /* CP15ISB, if > ARMv5TEJ */
118+
blx ENTRY_ADDR
119+
120+
/* Return back to FEL */
121+
b return_to_fel
122+
123+
cache_is_unsupported:
124+
/* Bail out if cache is enabled and change 'eGON.BT0' -> 'eGON.???' */
125+
ldr TMP1, cache_enabled_str
126+
str TMP1, [ENTRY_ADDR, #8]
127+
b return_to_fel_noswap
128+
129+
checksum_is_bad:
130+
/* The checksum test failed, so change 'eGON.BT0' -> 'eGON.BAD' */
131+
ldr TMP1, checksum_failed_str
132+
str TMP1, [ENTRY_ADDR, #8]
133+
134+
return_to_fel:
135+
bl swap_all_buffers
136+
return_to_fel_noswap:
137+
pop {TMP1, lr}
138+
msr cpsr_c, TMP1 /* Restore the original CPSR */
139+
ldr sp, [sp]
140+
bx lr
141+
142+
checksum_seed:
143+
.word 0x5f0a6c39
144+
egon_fel_str:
145+
.ascii ".FEL"
146+
cache_enabled_str:
147+
.ascii ".???"
148+
checksum_failed_str:
149+
.ascii ".BAD"
150+
151+
appended_data:
152+
/*
153+
* The appended data uses the following format:
154+
*
155+
* struct {
156+
* uint32_t entry_addr;
157+
* sram_swap_buffers swaptbl[];
158+
* };
159+
*
160+
* More details about the entry address and the 'sram_swap_buffers' struct
161+
* can be found in the 'fel.c' source file.
162+
*/

0 commit comments

Comments
 (0)