From mboxrd@z Thu Jan 1 00:00:00 1970 From: Hans de Goede Date: Thu, 05 Feb 2015 21:41:27 +0100 Subject: [U-Boot] [PATCH sunxi-tools] WIP: fel: Add a command for loading U-Boot SPL binaries in eGON format In-Reply-To: <20150205215449.15fa5b18@i7> References: <1423128983-28483-1-git-send-email-siarhei.siamashka@gmail.com> <20150205215449.15fa5b18@i7> Message-ID: <54D3D577.4070702@redhat.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Hi, On 05-02-15 20:54, Siarhei Siamashka wrote: > On Thu, 5 Feb 2015 11:36:23 +0200 > Siarhei Siamashka 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 >> --- >> 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 */ >> + 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 */ >> + 0xe3540000, /* 54: cmp r4, #0 */ >> + 0x1afffff1, /* 58: bne 24 */ >> + 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 */ >> + >> + /* 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); >