This unifies the FEL and SD card SPL binaries. And also removes
the FEL SPL size limit (now it can be as large as ~30 KiB).
This work had been inspired by the recent FEL mode support
u-boot patches from Simon Glass and Hans de Goede.
Signed-off-by: Siarhei Siamashka <siarhei.siamas...@gmail.com>
---
fel.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 189 insertions(+), 22 deletions(-)
diff --git a/fel.c b/fel.c
index c21d6ec..c0f28d8 100644
--- a/fel.c
+++ b/fel.c
@@ -41,6 +41,17 @@ struct aw_usb_request {
char pad[10];
} __attribute__((packed));
+struct aw_fel_version {
+ char signature[8];
+ uint32_t soc_id; /* 0x00162300 */
+ uint32_t unknown_0a; /* 1 */
+ uint16_t protocol; /* 1 */
+ uint8_t unknown_12; /* 0x44 */
+ uint8_t unknown_13; /* 0x08 */
+ uint32_t scratchpad; /* 0x7e00 */
+ uint32_t pad[2]; /* unused */
+} __attribute__((packed));
+
static const int AW_USB_READ = 0x11;
static const int AW_USB_WRITE = 0x12;
@@ -136,32 +147,27 @@ void aw_read_fel_status(libusb_device_handle *usb)
aw_usb_read(usb, &buf, sizeof(buf));
}
-void aw_fel_get_version(libusb_device_handle *usb)
+void aw_fel_get_version(libusb_device_handle *usb, struct aw_fel_version *buf)
{
- struct aw_fel_version {
- char signature[8];
- uint32_t soc_id; /* 0x00162300 */
- uint32_t unknown_0a; /* 1 */
- uint16_t protocol; /* 1 */
- uint8_t unknown_12; /* 0x44 */
- uint8_t unknown_13; /* 0x08 */
- uint32_t scratchpad; /* 0x7e00 */
- uint32_t pad[2]; /* unused */
- } __attribute__((packed)) buf;
-
aw_send_fel_request(usb, AW_FEL_VERSION, 0, 0);
- aw_usb_read(usb, &buf, sizeof(buf));
+ aw_usb_read(usb, buf, sizeof(*buf));
aw_read_fel_status(usb);
- buf.soc_id = le32toh(buf.soc_id);
- buf.unknown_0a = le32toh(buf.unknown_0a);
- buf.protocol = le32toh(buf.protocol);
- buf.scratchpad = le16toh(buf.scratchpad);
- buf.pad[0] = le32toh(buf.pad[0]);
- buf.pad[1] = le32toh(buf.pad[1]);
+ buf->soc_id = (le32toh(buf->soc_id) >> 8) & 0xFFFF;
+ buf->unknown_0a = le32toh(buf->unknown_0a);
+ buf->protocol = le32toh(buf->protocol);
+ buf->scratchpad = le16toh(buf->scratchpad);
+ buf->pad[0] = le32toh(buf->pad[0]);
+ buf->pad[1] = le32toh(buf->pad[1]);
+}
+
+void aw_fel_print_version(libusb_device_handle *usb)
+{
+ struct aw_fel_version buf;
+ aw_fel_get_version(usb, &buf);
const char *soc_name="unknown";
- switch ((buf.soc_id >> 8) & 0xFFFF) {
+ switch (buf.soc_id) {
case 0x1623: soc_name="A10";break;
case 0x1625: soc_name="A13";break;
case 0x1633: soc_name="A31";break;
@@ -170,7 +176,10 @@ void aw_fel_get_version(libusb_device_handle *usb)
case 0x1639: soc_name="A80";break;
}
- printf("%.8s soc=%08x(%s) %08x ver=%04x %02x %02x scratchpad=%08x %08x
%08x\n", buf.signature, buf.soc_id, soc_name, buf.unknown_0a, buf.protocol,
buf.unknown_12, buf.unknown_13, buf.scratchpad, buf.pad[0], buf.pad[1]);
+ printf("%.8s soc=%08x(%s) %08x ver=%04x %02x %02x scratchpad=%08x %08x
%08x\n",
+ buf.signature, buf.soc_id, soc_name, buf.unknown_0a,
+ buf.protocol, buf.unknown_12, buf.unknown_13,
+ buf.scratchpad, buf.pad[0], buf.pad[1]);
}
void aw_fel_read(libusb_device_handle *usb, uint32_t offset, void *buf,
size_t len)
@@ -285,6 +294,158 @@ void aw_fel_fill(libusb_device_handle *usb, uint32_t
offset, size_t size, unsign
aw_fel_write(usb, buf, offset, size);
}
+/*
+ * Information about usable areas in SRAM, which are not clashing with
+ * the FEL support code from BROM.
+ */
+
+typedef struct { uint32_t addr; uint32_t size; } sram_data_chunk;
+
+sram_data_chunk a10_a13_a20_sram_layout[] = {
+ { .addr = 0x00000, .size = 0x1800 }, /* Part of the IRQ handler stack */
+ { .addr = 0x02000, .size = 0x3D00 }, /* Part of the normal stack */
+ { .addr = 0x08000, .size = 0x3000 }, /* SRAM section A3 */
+ { 0, 0 }
+};
+
+sram_data_chunk a31_sram_layout[] = {
+ { .addr = 0x00000, .size = 0x1800 }, /* Part of the IRQ handler stack */
+ { .addr = 0x02000, .size = 0x3D00 }, /* Part of the normal stack */
+ { .addr = 0x44000, .size = 0x3000 }, /* SRAM section A2 */
+ { 0, 0 }
+};
+
+sram_data_chunk default_sram_layout[] = {
+ { .addr = 0x00000, .size = 0x1800 }, /* Part of the IRQ handler stack */
+ { .addr = 0x02000, .size = 0x3D00 }, /* Part of the normal stack */
+ { 0, 0 }
+};
+
+struct {
+ uint32_t soc_id;
+ sram_data_chunk *sram_layout;
+} soc_sram_layout[] = {
+ { .soc_id = 0x1623, .sram_layout = a10_a13_a20_sram_layout },
+ { .soc_id = 0x1625, .sram_layout = a10_a13_a20_sram_layout },
+ { .soc_id = 0x1651, .sram_layout = a10_a13_a20_sram_layout },
+ { .soc_id = 0x1633, .sram_layout = a31_sram_layout },
+ { 0, 0 }
+};
+
+sram_data_chunk *aw_fel_get_sram_layout(libusb_device_handle *usb)
+{
+ int i;
+ struct aw_fel_version buf;
+
+ aw_fel_get_version(usb, &buf);
+
+ for (i = 0; soc_sram_layout[i].sram_layout; i++)
+ if (soc_sram_layout[i].soc_id == buf.soc_id)
+ return soc_sram_layout[i].sram_layout;
+
+ return default_sram_layout;
+}
+
+static uint32_t spl_loader[] = {
+ /* Disable IRQ and FIQ */
+ 0xe10f0000, /* 0: mrs r0, CPSR */
+ 0xe38000c0, /* 4: orr r0, r0, #192 */
+ 0xe121f000, /* 8: msr CPSR_c, r0 */
+
+ /* Collect and glue pieces of SPL */
+ 0xe3066c39, /* c: movw r6, #27705 */
+ 0xe3456f0a, /* 10: movt r6, #24330 */
+ 0xe3a02010, /* 14: mov r2, #16 */
+ 0xe5924000, /* 18: ldr r4, [r2] */
+ 0xe3a00000, /* 1c: mov r0, #0 */
+ 0xe28f3068, /* 20: add r3, pc, #104 */
+ 0xe4931004, /* 24: ldr r1, [r3], #4 */
+ 0xe4935004, /* 28: ldr r5, [r3], #4 */
+ 0xe1550004, /* 2c: cmp r5, r4 */
+ 0xa1a05004, /* 30: movge r5, r4 */
+ 0xe0444005, /* 34: sub r4, r4, r5 */
+ 0xe3550000, /* 38: cmp r5, #0 */
+ 0x0a000006, /* 3c: beq 5c <entry_point+0x5c> */
+ 0xe4912004, /* 40: ldr r2, [r1], #4 */
+ 0xe2555004, /* 44: subs r5, r5, #4 */
+ 0xe4802004, /* 48: str r2, [r0], #4 */
+ 0xe0866002, /* 4c: add r6, r6, r2 */
+ 0x1afffffa, /* 50: bne 40 <entry_point+0x40> */
+ 0xe3540000, /* 54: cmp r4, #0 */
+ 0x1afffff1, /* 58: bne 24 <entry_point+0x24> */
+ 0xe3a0200c, /* 5c: mov r2, #12 */
+ 0xe5922000, /* 60: ldr r2, [r2] */
+
+ /* Verify the checksum */
+ 0xe0566082, /* 64: subs r6, r6, r2, lsl #1 */
+ 0x1a000003, /* 68: bne 7c <entry_point+0x7c> */
+
+ /* The checksum is good - jump to SPL */
+ 0xf57ff04f, /* 6c: dsb sy */
+ 0xf57ff06f, /* 70: isb sy */
+ 0xe3a00000, /* 74: mov r0, #0 */
+ 0xe12fff10, /* 78: bx r0 */
+
+ /* The checksum is bad - jump to FEL mode init in BROM */
+ 0xf57ff04f, /* 7c: dsb sy */
+ 0xf57ff06f, /* 80: isb sy */
+ 0xe3000020, /* 84: movw r0, #32 */
+ 0xe34f0fff, /* 88: movt r0, #65535 */
+ 0xe12fff10, /* 8c: bx r0 */
+};
+
+void aw_fel_write_and_execute_spl(libusb_device_handle *usb,
+ uint8_t *buf, size_t len)
+{
+ sram_data_chunk *sram_layout = aw_fel_get_sram_layout(usb);
+ size_t i;
+ uint32_t spl_checksum, spl_len;
+ uint32_t *buf32 = (uint32_t *)buf;
+
+ if (len < 32 || memcmp(buf + 4, "eGON.BT0", 8) != 0) {
+ fprintf(stderr, "Invalid file format: eGON header not found\n");
+ return;
+ }
+
+ spl_checksum = 2 * le32toh(buf32[3]) - 0x5F0A6C39;
+ spl_len = le32toh(buf32[4]);
+
+ if (spl_len > len || (spl_len % 4) != 0) {
+ fprintf(stderr, "Invalid file format: bad length\n");
+ return;
+ }
+
+ len = spl_len;
+ for (i = 0; i < len / 4; i++)
+ spl_checksum -= le32toh(buf32[i]);
+
+
+ if (spl_checksum != 0) {
+ fprintf(stderr, "Invalid file format: bad checksum\n");
+ return;
+ }
+
+ for (i = 0; len > 0 && sram_layout[i].size; i++) {
+ uint32_t chunk_addr = sram_layout[i].addr;
+ size_t chunk_size = sram_layout[i].size;
+ if (chunk_size > len)
+ chunk_size = len;
+ aw_fel_write(usb, buf, chunk_addr, chunk_size);
+ buf += chunk_size;
+ len -= chunk_size;
+ }
+
+ if (len > 0) {
+ printf("The SPL size (%d) is too large\n", spl_len);
+ return;
+ }
+
+ aw_fel_write(usb, spl_loader, 0x7e00, sizeof(spl_loader));
+ aw_fel_write(usb, sram_layout, 0x7e00 + sizeof(spl_loader),
+ (i + 1) * sizeof(*sram_layout));
+ aw_fel_execute(usb, 0x7e00);
+}
+
static int aw_fel_get_endpoint(libusb_device_handle *usb)
{
struct libusb_device *dev = libusb_get_device(usb);
@@ -343,6 +504,7 @@ int main(int argc, char **argv)
" ver[sion] Show BROM version\n"
" clear address length Clear memory\n"
" fill address length value Fill memory\n"
+ " spl file Load and execute U-Boot
SPL\n"
, argv[0]
);
}
@@ -387,7 +549,7 @@ int main(int argc, char **argv)
aw_fel_execute(handle, strtoul(argv[2], NULL, 0));
skip=3;
} else if (strncmp(argv[1], "ver", 3) == 0 && argc > 1) {
- aw_fel_get_version(handle);
+ aw_fel_print_version(handle);
skip=1;
} else if (strcmp(argv[1], "write") == 0 && argc > 3) {
size_t size;
@@ -408,6 +570,11 @@ int main(int argc, char **argv)
} else if (strcmp(argv[1], "fill") == 0 && argc > 3) {
aw_fel_fill(handle, strtoul(argv[2], NULL, 0),
strtoul(argv[3], NULL, 0), (unsigned char)strtoul(argv[4], NULL, 0));
skip=4;
+ } else if (strcmp(argv[1], "spl") == 0 && argc > 2) {
+ size_t size;
+ uint8_t *buf = load_file(argv[2], &size);
+ aw_fel_write_and_execute_spl(handle, buf, size);
+ skip=2;
} else {
fprintf(stderr,"Invalid command %s\n", argv[1]);
exit(1);