[U-Boot] [PATCH sunxi-tools] WIP: fel: Add a command for loading U-Boot SPL binaries in eGON format

Siarhei Siamashka siarhei.siamashka at gmail.com
Thu Feb 5 10:36:23 CET 2015


!!! Works only on Allwinner A20 so far and needs a bit more
!!! debugging. And even on A20, the transition from the SPL to
!!! the main U-boot binary has some glitches.

Now it should be possible to load and execute the same U-Boot SPL,
as used for booting from SD cards. The only argument for this new
command is the name of the SPL binary file (with eGON header).
It can be run as:

    fel spl u-boot-sunxi-with-spl.bin

The free space in SRAM is scattered in the FEL mode across multiple
locations. We just split SPL into individual chunks, upload them
to the free locations in SRAM and also upload a small startup code,
which is responsible for harvesting and gluing the pieces of SPL
together. Additionally, the eGON checksum is verified to ensure
that no data corruption has happened due to some unexpected clash
with the FEL protocol code from the BROM.

After this is done, it is not possible to return back to FEL anymore
(both IRQ and normal stacks of the BROM are overwritten). However
it is still possible to transfer control to the FEL init code (so
that it re-initializes the USB hardware and all the other things):

    /* Jump to the FEL entry point in BROM */
    movw  r0, #0x20
    movt  r0, #0xffff
    bx    r0

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.siamashka at 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);
-- 
2.0.5



More information about the U-Boot mailing list