Skip to content

Commit a706f0c

Browse files
committed
sunxi-fel: add TOC0 boot image support
Secure boot sunxi images use TOC0 containers instead of a bare eGON SPL header. Teach the spl/uboot command path to recognize a TOC0 image, validate its header and checksum, extract the firmware item, and wrap it in a synthetic eGON SPL header at the SoC SPL load address. After the wrapped SPL returns to FEL, pass the payload appended after the TOC0 container to the existing U-Boot image loader so FIT-based u-boot-sunxi-with-spl.bin images can be loaded by the normal uboot command path. Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
1 parent 3035186 commit a706f0c

1 file changed

Lines changed: 158 additions & 2 deletions

File tree

fel.c

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,38 @@ typedef struct image_header {
7676
#define HEADER_NAME_OFFSET offsetof(image_header_t, ih_name)
7777
#define HEADER_SIZE sizeof(image_header_t)
7878

79+
#define TOC0_HEADER_SIZE 0x30
80+
#define TOC0_ITEM_SIZE 0x20
81+
#define TOC0_MAIN_MAGIC_OFFSET 0x08
82+
#define TOC0_CHECKSUM_OFFSET 0x0c
83+
#define TOC0_NUM_ITEMS_OFFSET 0x18
84+
#define TOC0_LENGTH_OFFSET 0x1c
85+
#define TOC0_MAIN_END_OFFSET 0x2c
86+
#define TOC0_ITEM_NAME_OFFSET 0x00
87+
#define TOC0_ITEM_OFFSET_OFFSET 0x04
88+
#define TOC0_ITEM_SIZE_OFFSET 0x08
89+
#define TOC0_ITEM_LOAD_OFFSET 0x14
90+
#define TOC0_ITEM_END_OFFSET 0x1c
91+
#define TOC0_MAIN_INFO_MAGIC 0x89119800
92+
#define TOC0_ITEM_INFO_NAME_FIRMWARE 0x00010202
93+
#define EGON_HEADER_SIZE 0x60
94+
#define EGON_CHECKSUM_SEED 0x5F0A6C39
95+
#define SPL_HEADER_VERSION 2
96+
97+
static uint32_t get_le32(const void *ptr)
98+
{
99+
uint32_t val;
100+
101+
memcpy(&val, ptr, sizeof(val));
102+
return le32toh(val);
103+
}
104+
105+
static void put_le32(void *ptr, uint32_t val)
106+
{
107+
val = htole32(val);
108+
memcpy(ptr, &val, sizeof(val));
109+
}
110+
79111
/*
80112
* Utility function to determine the image type from a mkimage-compatible
81113
* header at given buffer (address).
@@ -775,7 +807,7 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
775807
if (len < 32 || memcmp(buf + 4, "eGON.BT0", 8) != 0)
776808
pr_fatal("SPL: eGON header is not found\n");
777809

778-
spl_checksum = 2 * le32toh(buf32[3]) - 0x5F0A6C39;
810+
spl_checksum = 2 * le32toh(buf32[3]) - EGON_CHECKSUM_SEED;
779811
spl_len = le32toh(buf32[4]);
780812

781813
if (spl_len > len || (spl_len % 4) != 0)
@@ -970,6 +1002,112 @@ static void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf,
9701002
uboot_size = data_size;
9711003
}
9721004

1005+
static bool is_toc0_image(const uint8_t *buf, size_t len)
1006+
{
1007+
return len >= TOC0_HEADER_SIZE && memcmp(buf, "TOC0.GLH", 8) == 0;
1008+
}
1009+
1010+
static uint8_t *toc0_make_egon_spl(const soc_info_t *soc_info,
1011+
const uint8_t *buf,
1012+
size_t len, size_t *egon_len,
1013+
size_t *payload_offset)
1014+
{
1015+
uint32_t toc0_len, num_items, checksum, hdr_checksum;
1016+
uint32_t code_offset = 0, code_size = 0, load_addr = 0;
1017+
uint32_t code_pos, branch_words;
1018+
uint64_t egon_size;
1019+
uint8_t *egon;
1020+
size_t i;
1021+
1022+
if (!soc_info)
1023+
pr_fatal("TOC0: Unsupported SoC type\n");
1024+
if (len < TOC0_HEADER_SIZE)
1025+
pr_fatal("TOC0: invalid header\n");
1026+
1027+
num_items = get_le32(buf + TOC0_NUM_ITEMS_OFFSET);
1028+
toc0_len = get_le32(buf + TOC0_LENGTH_OFFSET);
1029+
hdr_checksum = get_le32(buf + TOC0_CHECKSUM_OFFSET);
1030+
if ((toc0_len & 3) || toc0_len < TOC0_HEADER_SIZE ||
1031+
toc0_len > len || num_items == 0 ||
1032+
num_items > (toc0_len - TOC0_HEADER_SIZE) / TOC0_ITEM_SIZE)
1033+
pr_fatal("TOC0: invalid header\n");
1034+
if (get_le32(buf + TOC0_MAIN_MAGIC_OFFSET) != TOC0_MAIN_INFO_MAGIC ||
1035+
memcmp(buf + TOC0_MAIN_END_OFFSET, "MIE;", 4) != 0)
1036+
pr_fatal("TOC0: invalid main info header\n");
1037+
1038+
checksum = EGON_CHECKSUM_SEED;
1039+
for (i = 0; i < toc0_len; i += 4)
1040+
checksum += get_le32(buf + i);
1041+
if (checksum != 2 * hdr_checksum)
1042+
pr_fatal("TOC0: checksum mismatch\n");
1043+
1044+
for (i = 0; i < num_items; i++) {
1045+
const uint8_t *item = buf + TOC0_HEADER_SIZE +
1046+
i * TOC0_ITEM_SIZE;
1047+
uint32_t item_name;
1048+
uint32_t item_offset, item_size, item_load;
1049+
1050+
if (memcmp(item + TOC0_ITEM_END_OFFSET, "IIE;", 4) != 0)
1051+
pr_fatal("TOC0: invalid item header\n");
1052+
1053+
item_name = get_le32(item + TOC0_ITEM_NAME_OFFSET);
1054+
if (item_name != TOC0_ITEM_INFO_NAME_FIRMWARE)
1055+
continue;
1056+
1057+
item_offset = get_le32(item + TOC0_ITEM_OFFSET_OFFSET);
1058+
item_size = get_le32(item + TOC0_ITEM_SIZE_OFFSET);
1059+
item_load = get_le32(item + TOC0_ITEM_LOAD_OFFSET);
1060+
if (item_size == 0 || item_load == 0)
1061+
continue;
1062+
if (item_offset > toc0_len || item_size > toc0_len - item_offset)
1063+
pr_fatal("TOC0: invalid SPL item bounds\n");
1064+
1065+
code_offset = item_offset;
1066+
code_size = item_size;
1067+
load_addr = item_load;
1068+
break;
1069+
}
1070+
1071+
if (!code_size)
1072+
pr_fatal("TOC0: no loadable SPL item found\n");
1073+
if (load_addr < soc_info->spl_addr)
1074+
pr_fatal("TOC0: SPL load address 0x%08x is below 0x%08x\n",
1075+
load_addr, soc_info->spl_addr);
1076+
1077+
code_pos = load_addr - soc_info->spl_addr;
1078+
if (code_pos < EGON_HEADER_SIZE || (code_pos & 3))
1079+
pr_fatal("TOC0: unsupported SPL load offset 0x%x\n", code_pos);
1080+
1081+
egon_size = (uint64_t)code_pos + code_size;
1082+
egon_size = (egon_size + 3) & ~(uint64_t)3;
1083+
if (egon_size > soc_info->sram_size)
1084+
pr_fatal("TOC0: SPL item too large\n");
1085+
*egon_len = (size_t)egon_size;
1086+
egon = calloc(1, *egon_len);
1087+
if (!egon)
1088+
pr_fatal("TOC0: failed to allocate SPL buffer\n");
1089+
1090+
branch_words = (code_pos - 8) >> 2;
1091+
put_le32(egon, 0xea000000 | (branch_words & 0x00ffffff));
1092+
memcpy(egon + 4, "eGON.BT0", 8);
1093+
put_le32(egon + 0x10, (uint32_t)*egon_len);
1094+
memcpy(egon + 0x14, "SPL", 3);
1095+
egon[0x17] = SPL_HEADER_VERSION;
1096+
memcpy(egon + code_pos, buf + code_offset, code_size);
1097+
1098+
checksum = EGON_CHECKSUM_SEED;
1099+
for (i = 0; i < *egon_len; i += 4)
1100+
checksum += get_le32(egon + i);
1101+
put_le32(egon + 0x0c, checksum);
1102+
1103+
*payload_offset = toc0_len;
1104+
pr_info("TOC0: wrapped SPL item %u bytes from 0x%x, load 0x%08x\n",
1105+
code_size, code_offset, load_addr);
1106+
pr_info("TOC0: payload starts at 0x%x\n", toc0_len);
1107+
1108+
return egon;
1109+
}
1110+
9731111
static const char *spl_get_dtb_name(uint8_t *spl_buf)
9741112
{
9751113
uint32_t dt_offset;
@@ -1001,7 +1139,25 @@ void aw_fel_process_spl_and_uboot(feldev_handle *dev, const char *filename)
10011139
uint32_t offset;
10021140
/* load file into memory buffer */
10031141
uint8_t *buf = load_file(filename, &size);
1004-
const char *dt_name = spl_get_dtb_name(buf);
1142+
const char *dt_name;
1143+
1144+
if (is_toc0_image(buf, size)) {
1145+
size_t egon_spl_size, payload_offset;
1146+
uint8_t *egon_spl = toc0_make_egon_spl(dev->soc_info, buf,
1147+
size, &egon_spl_size,
1148+
&payload_offset);
1149+
1150+
aw_fel_write_and_execute_spl(dev, egon_spl, egon_spl_size);
1151+
free(egon_spl);
1152+
1153+
if (size > payload_offset)
1154+
aw_fel_write_uboot_image(dev, buf + payload_offset,
1155+
size - payload_offset, NULL);
1156+
free(buf);
1157+
return;
1158+
}
1159+
1160+
dt_name = spl_get_dtb_name(buf);
10051161

10061162
/* write and execute the SPL from the buffer */
10071163
offset = aw_fel_write_and_execute_spl(dev, buf, size);

0 commit comments

Comments
 (0)