[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