A patch to extend the "fel spl" command to properly handle not only the SPL part of a combined u-boot-sunxi-with-spl.bin boot file, but additionally also transfer the main u-boot binary (image) contained within the second part of such files.
This can simplify the boot process / steps required to get a proper FEL setup - e.g. it avoids having to select u-boot.bin vs. u-boot-dtb.bin, see e.g. http://lists.denx.de/pipermail/u-boot/2015-June/217476.html On top of that a new "fel uboot" command is introduced. As per suggestions on the mailing list, it will take such a combined (SPL + U-Boot) file and actually start U-Boot execution before the program exits. "uboot" may thus also be combined with other "write" commands to transfer additional files required for the boot (e.g. boot script, kernel, initrd). Original patch/RFC was here: https://groups.google.com/forum/#!topic/linux-sunxi/wBEGUoLNRro Signed-off-by: Bernhard Nortmann <[email protected]> --- fel.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/fel.c b/fel.c index adf8f35..8a7974c 100644 --- a/fel.c +++ b/fel.c @@ -62,6 +62,9 @@ static int AW_USB_FEL_BULK_EP_OUT; static int AW_USB_FEL_BULK_EP_IN; static int timeout = 60000; static int verbose = 0; /* Makes the 'fel' tool more talkative if non-zero */ +static uint32_t uboot_entry = 0; /* entry point (address) of U-Boot */ +static uint32_t uboot_size = 0; /* size of U-Boot binary */ +static int uboot_autostart = 0; /* "uboot" command flag = autostart U-Boot */ static void pr_info(const char *fmt, ...) { @@ -208,6 +211,13 @@ void aw_fel_read(libusb_device_handle *usb, uint32_t offset, void *buf, size_t l void aw_fel_write(libusb_device_handle *usb, void *buf, uint32_t offset, size_t len) { + /* safeguard against overwriting an already loaded U-Boot binary */ + if (uboot_size > 0 && offset <= uboot_entry + uboot_size && offset + len >= uboot_entry) { + fprintf(stderr, "ERROR: Attempt to overwrite U-Boot! " + "Request 0x%08X-0x%08X overlaps 0x%08X-0x%08X.\n", + offset, offset + len, uboot_entry, uboot_entry + uboot_size); + exit(1); + } aw_send_fel_request(usb, AW_FEL_1_WRITE, offset, len); aw_usb_write(usb, buf, len); aw_read_fel_status(usb); @@ -599,6 +609,11 @@ void aw_restore_and_enable_mmu(libusb_device_handle *usb, uint32_t *tt) free(tt); } +/* + * Maximum size of SPL, at the same time this is the start offset + * of the main U-Boot image within u-boot-sunxi-with-spl.bin + */ +static const int SPL_LEN_LIMIT = 0x8000; void aw_fel_write_and_execute_spl(libusb_device_handle *usb, uint8_t *buf, size_t len) @@ -608,7 +623,7 @@ void aw_fel_write_and_execute_spl(libusb_device_handle *usb, char header_signature[9] = { 0 }; size_t i, thunk_size; uint32_t *thunk_buf; - uint32_t spl_checksum, spl_len, spl_len_limit = 0x8000; + uint32_t spl_checksum, spl_len, spl_len_limit = SPL_LEN_LIMIT; uint32_t *buf32 = (uint32_t *)buf; uint32_t written = 0; uint32_t *tt = NULL; @@ -722,6 +737,85 @@ void aw_fel_write_and_execute_spl(libusb_device_handle *usb, aw_restore_and_enable_mmu(usb, tt); } +/* Constants taken from ${U-BOOT}/include/image.h */ +#define IH_MAGIC 0x27051956 /* Image Magic Number */ +#define IH_ARCH_ARM 2 /* ARM */ +#define IH_TYPE_FIRMWARE 5 /* Firmware Image */ +#define IH_NMLEN 32 /* Image Name Length */ + +#define HEADER_NAME_OFFSET 32 /* offset of name field */ +#define HEADER_SIZE (HEADER_NAME_OFFSET + IH_NMLEN) + +/* + * This function tests a given buffer address and length for a valid U-Boot + * image. Upon success, the image data gets transferred to the default memory + * address stored within the image header; and the function preserves the + * U-Boot entry point (offset) and size values. + */ +void aw_fel_write_uboot_image(libusb_device_handle *usb, + uint8_t *buf, size_t len) +{ + if (len <= HEADER_SIZE) + return; /* Insufficient size (no actual data), just bail out */ + + /* Check for a valid mkimage header */ + uint32_t *buf32 = (uint32_t *)buf; + + if (be32toh(buf32[0]) != IH_MAGIC) { + fprintf(stderr, "U-Boot image verification failure: " + "expected IH_MAGIC, got 0x%X\n", be32toh(buf32[0])); + exit(1); + } + if (buf[29] != IH_ARCH_ARM|| buf[30] != IH_TYPE_FIRMWARE) { + fprintf(stderr, "U-Boot image verification failure: " + "expected ARM firmware, got %02X %02X\n", buf[29], buf[30]); + exit(1); + } + uint32_t data_size = be32toh(buf32[3]); /* Image Data Size */ + uint32_t load_addr = be32toh(buf32[4]); /* Data Load Address */ + if ((size_t)data_size != len - HEADER_SIZE) { + fprintf(stderr, "U-Boot image data size mismatch: " + "expected %d, got %u\n", len - HEADER_SIZE, data_size); + exit(1); + } + /* TODO: Verify image data integrity using the checksum field ih_dcrc, + * available from be32toh(buf32[6]) + * + * However, this requires CRC routines that mimic their U-Boot + * counterparts, namely image_check_dcrc() in ${U-BOOT}/common/image.cabs + * and crc_wd() in ${U-BOOT}/lib/crc32.c + * + * It should be investigated if existing CRC routines in sunxi-tools + * could be factored out and reused for this purpose - e.g. calc_crc32() + * from nand-part-main.c + */ + + /* If we get here, we're "good to go" (i.e. actually write the data) */ + pr_info("Writing image \"%.*s\", %u bytes @ 0x%08X.\n", + IH_NMLEN, buf + HEADER_NAME_OFFSET, data_size, load_addr); + + aw_fel_write(usb, buf + HEADER_SIZE, load_addr, data_size); + + /* keep track of U-Boot memory region in global vars */ + uboot_entry = load_addr; + uboot_size = data_size; +} + +/* + * This function handles the common part of both "spl" and "uboot" commands. + */ +void aw_fel_process_spl_and_uboot(libusb_device_handle *usb, + const char *filename) +{ + /* load file into memory buffer */ + size_t size; + uint8_t *buf = load_file(filename, &size); + /* write and execute the SPL from the buffer */ + aw_fel_write_and_execute_spl(usb, buf, size); + /* check for optional main U-Boot binary (and transfer it, if applicable) */ + aw_fel_write_uboot_image(usb, buf + SPL_LEN_LIMIT, size - SPL_LEN_LIMIT); +} + static int aw_fel_get_endpoint(libusb_device_handle *usb) { struct libusb_device *dev = libusb_get_device(usb); @@ -781,6 +875,17 @@ int main(int argc, char **argv) if (argc <= 1) { printf("Usage: %s [options] command arguments... [command...]\n" " -v, --verbose Verbose logging\n" + "\n" + " spl file Load and execute U-Boot SPL\n" + " If file additionally contains a main U-Boot binary\n" + " (u-boot-sunxi-with-spl.bin), this command also transfers that\n" + " to memory (default address from image), but won't execute it.\n" + "\n" + " uboot file-with-spl like \"spl\", but actually starts U-Boot\n" + " U-Boot execution will take place when the fel utility exits.\n" + " This allows combining \"uboot\" with further \"write\" commands\n" + " (to transfer other files needed for the boot).\n" + "\n" " hex[dump] address length Dumps memory region in hex\n" " dump address length Binary memory dump\n" " exe[cute] address Call function address\n" @@ -789,7 +894,6 @@ 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] ); } @@ -870,9 +974,13 @@ int main(int argc, char **argv) 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); + aw_fel_process_spl_and_uboot(handle, argv[2]); + skip=2; + } else if (strcmp(argv[1], "uboot") == 0 && argc > 2) { + aw_fel_process_spl_and_uboot(handle, argv[2]); + uboot_autostart = (uboot_entry > 0 && uboot_size > 0); + if (!uboot_autostart) + printf("Warning: \"uboot\" command failed to detect image! Can't execute U-Boot.\n"); skip=2; } else { fprintf(stderr,"Invalid command %s\n", argv[1]); @@ -882,6 +990,12 @@ int main(int argc, char **argv) argv+=skip; } + // auto-start U-Boot if requested (by the "uboot" command) + if (uboot_autostart) { + pr_info("Starting U-Boot (0x%08X).\n", uboot_entry); + aw_fel_execute(handle, uboot_entry); + } + #if defined(__linux__) if (iface_detached >= 0) libusb_attach_kernel_driver(handle, iface_detached); -- 2.0.5 -- You received this message because you are subscribed to the Google Groups "linux-sunxi" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
