[U-Boot] [PATCH sunxi-tools] WIP: fel: Add a command for loading U-Boot SPL binaries in eGON format
Hans de Goede
hdegoede at redhat.com
Thu Feb 5 21:41:27 CET 2015
Hi,
On 05-02-15 20:54, Siarhei Siamashka wrote:
> On Thu, 5 Feb 2015 11:36:23 +0200
> Siarhei Siamashka <siarhei.siamashka at 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 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);
>
More information about the U-Boot
mailing list