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

!!! 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@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);

On Thu, 5 Feb 2015 11:36:23 +0200 Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
!!! 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
BTW, we still have the plan B in the case if the jump to 0xffff0020 turns out to be way too problematic.
Essentially, the problem that we want to solve here is to ensure a sufficiently large and consistent SRAM address space for the SPL without any potentially SoC variant specific holes in the case of booting over USB via FEL.
This can be achieved by injecting special entry/exit code fragments, which would reshuffle data in SRAM to provide a contiguous space for the SPL at the beginning of SRAM and preserve the data from the BROM stacks in the higher addresses of SRAM. Then move the BROM stacks back into the original place just before returning control to FEL (using the return address from the 'lr' register as usual). Doing it this way, we get the current FEL boot behaviour, except that dealing with the SRAM address space layout is abstracted in the 'fel' tool and the SPL can just always use the 0x20 entry point and a big contiguous area in the lower addresses of SRAM. The available SRAM space will be less than 32 KiB though (but at least larger than 16 KiB), because the backups of the BROM stacks have to be preserved.
Either way, I hope to send some fully working solution tomorrow to replace this proof-of-concept patch.
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@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);
} else if (strcmp(argv[1], "write") == 0 && argc > 3) { size_t size;aw_fel_print_version(handle); skip=1;
@@ -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);
} else { fprintf(stderr,"Invalid command %s\n", argv[1]); exit(1);skip=2;

Hi,
On 05-02-15 20:54, Siarhei Siamashka wrote:
On Thu, 5 Feb 2015 11:36:23 +0200 Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
!!! 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
BTW, we still have the plan B in the case if the jump to 0xffff0020 turns out to be way too problematic.
Essentially, the problem that we want to solve here is to ensure a sufficiently large and consistent SRAM address space for the SPL without any potentially SoC variant specific holes in the case of booting over USB via FEL.
This can be achieved by injecting special entry/exit code fragments, which would reshuffle data in SRAM to provide a contiguous space for the SPL at the beginning of SRAM and preserve the data from the BROM stacks in the higher addresses of SRAM. Then move the BROM stacks back into the original place just before returning control to FEL (using the return address from the 'lr' register as usual). Doing it this way, we get the current FEL boot behaviour, except that dealing with the SRAM address space layout is abstracted in the 'fel' tool and the SPL can just always use the 0x20 entry point and a big contiguous area in the lower addresses of SRAM. The available SRAM space will be less than 32 KiB though (but at least larger than 16 KiB), because the backups of the BROM stacks have to be preserved.
Cool, I like the idea of saving the BROM data to some other SRAM area, and then restoring it before returning to FEL, I think that is better then re-initializing the usb connection each step. If only the BROM would use a more sane memory layout to begin with ...
Either way, I hope to send some fully working solution tomorrow to replace this proof-of-concept patch.
Cool, thanks for your work on this.
Regards,
Hans
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@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);
} else if (strcmp(argv[1], "write") == 0 && argc > 3) { size_t size;aw_fel_print_version(handle); skip=1;
@@ -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);
} else { fprintf(stderr,"Invalid command %s\n", argv[1]); exit(1);skip=2;

On Thu, 05 Feb 2015 21:41:27 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hi,
On 05-02-15 20:54, Siarhei Siamashka wrote:
On Thu, 5 Feb 2015 11:36:23 +0200 Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
!!! 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
BTW, we still have the plan B in the case if the jump to 0xffff0020 turns out to be way too problematic.
Essentially, the problem that we want to solve here is to ensure a sufficiently large and consistent SRAM address space for the SPL without any potentially SoC variant specific holes in the case of booting over USB via FEL.
This can be achieved by injecting special entry/exit code fragments, which would reshuffle data in SRAM to provide a contiguous space for the SPL at the beginning of SRAM and preserve the data from the BROM stacks in the higher addresses of SRAM. Then move the BROM stacks back into the original place just before returning control to FEL (using the return address from the 'lr' register as usual). Doing it this way, we get the current FEL boot behaviour, except that dealing with the SRAM address space layout is abstracted in the 'fel' tool and the SPL can just always use the 0x20 entry point and a big contiguous area in the lower addresses of SRAM. The available SRAM space will be less than 32 KiB though (but at least larger than 16 KiB), because the backups of the BROM stacks have to be preserved.
Cool, I like the idea of saving the BROM data to some other SRAM area, and then restoring it before returning to FEL, I think that is better then re-initializing the usb connection each step.
If only the BROM would use a more sane memory layout to begin with ...
Either way, I hope to send some fully working solution tomorrow to replace this proof-of-concept patch.
Cool, thanks for your work on this.
https://github.com/ssvb/sunxi-tools/commits/20150206-fel-large-spl-support
All of this seems to work very nicely. Now I can, for example, even boot regular old SD-card based SPL files over FEL, and they successfully pick up the main u-boot binary and the rest of the system from the SD card.
The SPL files built from updated u-boot (using my local hacks, based on Simon's patches) naturally can differentiate between SD and FEL boot at runtime.

Hi Siarhei,
On 5 February 2015 at 02:36, Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
!!! 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
This looks great! Does it write only the SPL portion or does it write U-Boot also?
I don't really understand how it works but will leave that to you and Hans.
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@gmail.com
[snip]
Regards, Simon

On Thu, 5 Feb 2015 22:49:39 -0700 Simon Glass sjg@chromium.org wrote:
Hi Siarhei,
On 5 February 2015 at 02:36, Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
!!! 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
This looks great! Does it write only the SPL portion or does it write U-Boot also?
Yes, it only takes care of the SPL part at the moment, but surely this can be improved later.
The biggest inconvenience is that we need to provide the 'fel' tool with with the magic address for the main u-boot binary. This address is hidden in the u-boot sources:
http://git.denx.de/?p=u-boot.git;a=blob;f=include/configs/sunxi-common.h;h=6...
But the users need to know it in order to set in as the command line argument for the 'fel' tool.
We could probably do a better job and store the necessary addresses in the SPL header. Currently 'mksunxiboot' provides only a minimalistic header with the "eGON.BT0" signature and a checksum, because otherwise it will refuse to boot from the SD card:
http://git.denx.de/?p=u-boot.git;a=blob;f=tools/mksunxiboot.c
But probably we can extend this header and also add the CONFIG_SYS_TEXT_BASE value there. Together with "kernel_addr_r", "fdt_addr_r", "scriptaddr" and "ramdisk_addr_r" values. After this change, the 'fel' tool will be able to extract all the necessary information from u-boot-sunxi-with-spl.bin file without relying on the command line too much. Moreover, these addresses are sometimes changing, and some users are IMHO rightfully upset whenever this happens:
http://permalink.gmane.org/gmane.comp.hardware.netbook.arm.sunxi/14919
I don't really understand how it works but will leave that to you and Hans.
OK :) Anyway, now I have finally pushed a supposedly properly working implementation to the following github branch:
https://github.com/ssvb/sunxi-tools/commits/20150206-fel-large-spl-support
Yes, we can handle most of this stuff with Hans. But some communication with you is still needed. In particular, we still need to decide a way to distinguish between booting from the SD card and over USB. Your RFC/WIP patch from http://lists.denx.de/pipermail/u-boot/2015-February/203858.html was checking the value in the 'lr' register, but this is not going to work anymore. To address this, right now I'm just replacing the "eGON.BT0" signature in memory to a new "eGON.FEL" signature at the address 0x0004. The idea is that this signature is critical for booting from the SD card. If there is some other value instead, then we are pretty sure that the SPL was not booted from the SD card. And having it as "eGON.FEL" serves as an indication of what exactly it is.
Additionally, we might even not need to store the "lr" and "sp" registers in save_boot_params(). If we settle on using the "eGON.FEL" signature in memory for distinguishing between the FEL and the SD boot, then we can also reuse the other parts of of header (for example, the checksum) to store the return address.
As you have figured out earlier, avoiding setting VBAR and CP15 stuff is important. Right now the FEL code in the BROM has an active interrupt handler in use (which I'm suppressing by disabling interrupts early) and the SPL needs to keep interrupts disabled, while trying not to mess its configuration.

Hi,
On 02/07/2015 04:17 AM, Siarhei Siamashka wrote:
On Thu, 5 Feb 2015 22:49:39 -0700 Simon Glass sjg@chromium.org wrote:
Hi Siarhei,
On 5 February 2015 at 02:36, Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
!!! 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
This looks great! Does it write only the SPL portion or does it write U-Boot also?
Yes, it only takes care of the SPL part at the moment, but surely this can be improved later.
The biggest inconvenience is that we need to provide the 'fel' tool with with the magic address for the main u-boot binary. This address is hidden in the u-boot sources:
http://git.denx.de/?p=u-boot.git;a=blob;f=include/configs/sunxi-common.h;h=6cfd7e148900#l28
But the users need to know it in order to set in as the command line argument for the 'fel' tool.
We could probably do a better job and store the necessary addresses in the SPL header. Currently 'mksunxiboot' provides only a minimalistic header with the "eGON.BT0" signature and a checksum, because otherwise it will refuse to boot from the SD card:
http://git.denx.de/?p=u-boot.git;a=blob;f=tools/mksunxiboot.c
But probably we can extend this header and also add the CONFIG_SYS_TEXT_BASE value there. Together with "kernel_addr_r", "fdt_addr_r", "scriptaddr" and "ramdisk_addr_r" values. After this change, the 'fel' tool will be able to extract all the necessary information from u-boot-sunxi-with-spl.bin file without relying on the command line too much. Moreover, these addresses are sometimes changing, and some users are IMHO rightfully upset whenever this happens:
I think that adding a new header for this sounds like a good idea, this header should start with some 8 byte magic marker so that the fel tool can be 100% sure if it is there.
http://permalink.gmane.org/gmane.comp.hardware.netbook.arm.sunxi/14919
I don't really understand how it works but will leave that to you and Hans.
OK :) Anyway, now I have finally pushed a supposedly properly working implementation to the following github branch:
https://github.com/ssvb/sunxi-tools/commits/20150206-fel-large-spl-support
Cool (note I've not looked at the actual code yet), this all sounds quite nice.
It would be really cool if when the new header is present and with a new enough fel tool + u-boot bin we could do:
fel spl u-boot-sunxi-with-spl.bin zImage - dtb
or:
fel spl u-boot-sunxi-with-spl.bin zImage initrd dtb
And everything would just work, note that the "zImage initrd dtb" order is the same as u-boot's bootm command, and the - is there to indicate there is no initrd.
Maybe we should also do something for old kernels where script.bin needs to be loaded, maybe:
fel spl-old-kernel ... ??
Yes, we can handle most of this stuff with Hans. But some communication with you is still needed. In particular, we still need to decide a way to distinguish between booting from the SD card and over USB. Your RFC/WIP patch from http://lists.denx.de/pipermail/u-boot/2015-February/203858.html was checking the value in the 'lr' register, but this is not going to work anymore. To address this, right now I'm just replacing the "eGON.BT0" signature in memory to a new "eGON.FEL" signature at the address 0x0004. The idea is that this signature is critical for booting from the SD card. If there is some other value instead, then we are pretty sure that the SPL was not booted from the SD card. And having it as "eGON.FEL" serves as an indication of what exactly it is.
I think that replacing eGON.BT0 with eGON.FEL when loading the SPL through the new mode is a good way of doing this, ACK to that.
Additionally, we might even not need to store the "lr" and "sp" registers in save_boot_params(). If we settle on using the "eGON.FEL" signature in memory for distinguishing between the FEL and the SD boot, then we can also reuse the other parts of of header (for example, the checksum) to store the return address.
We'll have to see how this all works out to determine whether or not that is a good idea.
As you have figured out earlier, avoiding setting VBAR and CP15 stuff is important. Right now the FEL code in the BROM has an active interrupt handler in use (which I'm suppressing by disabling interrupts early) and the SPL needs to keep interrupts disabled, while trying not to mess its configuration.
Ack.
Regards,
Hans

On Sat, 07 Feb 2015 10:21:46 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hi,
On 02/07/2015 04:17 AM, Siarhei Siamashka wrote:
On Thu, 5 Feb 2015 22:49:39 -0700 Simon Glass sjg@chromium.org wrote:
Hi Siarhei,
On 5 February 2015 at 02:36, Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
!!! 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
This looks great! Does it write only the SPL portion or does it write U-Boot also?
Yes, it only takes care of the SPL part at the moment, but surely this can be improved later.
The biggest inconvenience is that we need to provide the 'fel' tool with with the magic address for the main u-boot binary. This address is hidden in the u-boot sources:
http://git.denx.de/?p=u-boot.git;a=blob;f=include/configs/sunxi-common.h;h=6cfd7e148900#l28
But the users need to know it in order to set in as the command line argument for the 'fel' tool.
We could probably do a better job and store the necessary addresses in the SPL header. Currently 'mksunxiboot' provides only a minimalistic header with the "eGON.BT0" signature and a checksum, because otherwise it will refuse to boot from the SD card:
http://git.denx.de/?p=u-boot.git;a=blob;f=tools/mksunxiboot.c
But probably we can extend this header and also add the CONFIG_SYS_TEXT_BASE value there. Together with "kernel_addr_r", "fdt_addr_r", "scriptaddr" and "ramdisk_addr_r" values. After this change, the 'fel' tool will be able to extract all the necessary information from u-boot-sunxi-with-spl.bin file without relying on the command line too much. Moreover, these addresses are sometimes changing, and some users are IMHO rightfully upset whenever this happens:
I think that adding a new header for this sounds like a good idea, this header should start with some 8 byte magic marker so that the fel tool can be 100% sure if it is there.
http://permalink.gmane.org/gmane.comp.hardware.netbook.arm.sunxi/14919
I don't really understand how it works but will leave that to you and Hans.
OK :) Anyway, now I have finally pushed a supposedly properly working implementation to the following github branch:
https://github.com/ssvb/sunxi-tools/commits/20150206-fel-large-spl-support
Cool (note I've not looked at the actual code yet), this all sounds quite nice.
It would be really cool if when the new header is present and with a new enough fel tool + u-boot bin we could do:
That's the (longer term) plan.
fel spl u-boot-sunxi-with-spl.bin zImage - dtb
or:
fel spl u-boot-sunxi-with-spl.bin zImage initrd dtb
And everything would just work, note that the "zImage initrd dtb" order is the same as u-boot's bootm command, and the - is there to indicate there is no initrd.
I would perhaps prefer to keep the current 'spl' command name just for the SPL alone. And invent some new name for a more advanced command to use it with the future u-boot v2015.07 release.
Maybe we should also do something for old kernels where script.bin needs to be loaded, maybe:
fel spl-old-kernel ... ??
Isn't the script.bin normally loaded exactly at the same address as the dtb? So nothing special is really needed for old kernels here.
Yes, we can handle most of this stuff with Hans. But some communication with you is still needed. In particular, we still need to decide a way to distinguish between booting from the SD card and over USB. Your RFC/WIP patch from http://lists.denx.de/pipermail/u-boot/2015-February/203858.html was checking the value in the 'lr' register, but this is not going to work anymore. To address this, right now I'm just replacing the "eGON.BT0" signature in memory to a new "eGON.FEL" signature at the address 0x0004. The idea is that this signature is critical for booting from the SD card. If there is some other value instead, then we are pretty sure that the SPL was not booted from the SD card. And having it as "eGON.FEL" serves as an indication of what exactly it is.
I think that replacing eGON.BT0 with eGON.FEL when loading the SPL through the new mode is a good way of doing this, ACK to that.
Additionally, we might even not need to store the "lr" and "sp" registers in save_boot_params(). If we settle on using the "eGON.FEL" signature in memory for distinguishing between the FEL and the SD boot, then we can also reuse the other parts of of header (for example, the checksum) to store the return address.
We'll have to see how this all works out to determine whether or not that is a good idea.
As you have figured out earlier, avoiding setting VBAR and CP15 stuff is important. Right now the FEL code in the BROM has an active interrupt handler in use (which I'm suppressing by disabling interrupts early) and the SPL needs to keep interrupts disabled, while trying not to mess its configuration.
Ack.
Anyway, I think that we already have a solution for v2015.04 And unless unexpected problems are identified in the current implementation, I don't have plans to do any additional work on this FEL boot stuff at least for a few weeks. Anyone is welcome to pick it up from this point.
The linux-sunxi wiki is back from read-only mode. And I have edited the http://linux-sunxi.org/FEL/USBBoot page to add more up to date instructions. I think, this wraps it up.
participants (3)
-
Hans de Goede
-
Siarhei Siamashka
-
Simon Glass