diff --git a/arch/arm/mach-omap/Kconfig b/arch/arm/mach-omap/Kconfig index c865423..abf204a 100644 --- a/arch/arm/mach-omap/Kconfig +++ b/arch/arm/mach-omap/Kconfig @@ -120,6 +120,21 @@ You need the utility program omap4_usbboot to boot from USB. Please read omap4_usb_booting.txt for more information. +config OMAP3_USBBOOT + bool "enable booting from USB" + depends on ARCH_OMAP3 + help + Say Y here if you want to be able to boot the 2nd stage via USB. This + works by transferring the 2nd stage image using the MUSB controller + which is already initialized by the ROM code. Use the omap3-usb-loader + tool selectable below to upload images. + +config OMAP3_USB_LOADER + bool "enable omap3 USB loader host tool" + depends on ARCH_OMAP3 + help + Say Y here to build the omap3 usb loader tool. + config OMAP_SERIALBOOT bool "enable booting from serial" select XYMODEM diff --git a/arch/arm/mach-omap/Makefile b/arch/arm/mach-omap/Makefile index fcc2727..65072b9 100644 --- a/arch/arm/mach-omap/Makefile +++ b/arch/arm/mach-omap/Makefile @@ -31,6 +31,8 @@ obj-$(CONFIG_SHELL_NONE) += xload.o obj-$(CONFIG_MFD_TWL6030) += omap4_twl6030_mmc.o obj-$(CONFIG_OMAP4_USBBOOT) += omap4_rom_usb.o +obj-$(CONFIG_OMAP3_USBBOOT) += omap3_xload_usb.o +pbl-$(CONFIG_OMAP3_USBBOOT) += omap3_xload_usb.o obj-$(CONFIG_CMD_BOOT_ORDER) += boot_order.o obj-$(CONFIG_BAREBOX_UPDATE_AM33XX_SPI_NOR_MLO) += am33xx_bbu_spi_mlo.o obj-$(CONFIG_BAREBOX_UPDATE_AM33XX_NAND) += am33xx_bbu_nand.o diff --git a/arch/arm/mach-omap/include/mach/omap3-generic.h b/arch/arm/mach-omap/include/mach/omap3-generic.h index 7db0838..ab53b98 100644 --- a/arch/arm/mach-omap/include/mach/omap3-generic.h +++ b/arch/arm/mach-omap/include/mach/omap3-generic.h @@ -29,4 +29,6 @@ int omap3_init(void); int omap3_devices_init(void); +void *omap3_xload_boot_usb(void); + #endif /* __MACH_OMAP3_GENERIC_H */ diff --git a/arch/arm/mach-omap/omap3_xload_usb.c b/arch/arm/mach-omap/omap3_xload_usb.c new file mode 100644 index 0000000..e7dc21e --- /dev/null +++ b/arch/arm/mach-omap/omap3_xload_usb.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2015 Sascha Hauer , Pengutronix + * + * Based on a patch by: + * + * Copyright (C) 2011 Rick Bronson + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +static void __iomem *omap3_usb_base = (void __iomem *)OMAP3_MUSB0_BASE; + +#define OMAP34XX_USB_EP(n) (omap3_usb_base + 0x100 + 0x10 * (n)) + +#define MUSB_RXCSR 0x06 +#define MUSB_RXCOUNT 0x08 +#define MUSB_TXCSR 0x02 +#define MUSB_FIFOSIZE 0x0F +#define OMAP34XX_USB_RXCSR(n) (OMAP34XX_USB_EP(n) + MUSB_RXCSR) +#define OMAP34XX_USB_RXCOUNT(n) (OMAP34XX_USB_EP(n) + MUSB_RXCOUNT) +#define OMAP34XX_USB_TXCSR(n) (OMAP34XX_USB_EP(n) + MUSB_TXCSR) +#define OMAP34XX_USB_FIFOSIZE(n) (OMAP34XX_USB_EP(n) + MUSB_FIFOSIZE) +#define OMAP34XX_USB_FIFO(n) (omap3_usb_base + 0x20 + ((n) * 4)) + +/* memory mapped registers */ +#define BULK_ENDPOINT 1 +#define MUSB_RXCSR_RXPKTRDY 0x0001 +#define MUSB_TXCSR_TXPKTRDY 0x0001 + +#define PACK4(a,b,c,d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a)) +#define USBLOAD_CMD_FILE PACK4('U', 'S', 'B', 's') /* send file size */ +#define USBLOAD_CMD_JUMP PACK4('U', 'S', 'B', 'j') /* go where I tell you */ +#define USBLOAD_CMD_FILE_REQ PACK4('U', 'S', 'B', 'f') /* file request */ +#define USBLOAD_CMD_ECHO_SZ PACK4('U', 'S', 'B', 'n') /* echo file size */ +#define USBLOAD_CMD_REPORT_SZ PACK4('U', 'S', 'B', 'o') /* report file size */ +#define USBLOAD_CMD_MESSAGE PACK4('U', 'S', 'B', 'm') /* message for debug */ + +static int usb_send(unsigned char *buffer, unsigned int buffer_size) +{ + unsigned int cntr; + u16 txcsr; + void __iomem *reg = (void *)OMAP34XX_USB_TXCSR(BULK_ENDPOINT); + void __iomem *bulk_fifo = (void *)OMAP34XX_USB_FIFO(BULK_ENDPOINT); + + txcsr = readw(reg); + + if (txcsr & MUSB_TXCSR_TXPKTRDY) + return 0; + + for (cntr = 0; cntr < buffer_size; cntr++) + writeb(buffer[cntr], bulk_fifo); + + txcsr = readw(reg); + txcsr |= MUSB_TXCSR_TXPKTRDY; + writew(txcsr, reg); + + return buffer_size; +} + +static int usb_recv(u8 *buffer) +{ + int cntr; + u16 count = 0; + u16 rxcsr; + void __iomem *reg = (void *)OMAP34XX_USB_RXCSR(BULK_ENDPOINT); + void __iomem *bulk_fifo = (void *)OMAP34XX_USB_FIFO(BULK_ENDPOINT); + + rxcsr = readw(reg); + + if (!(rxcsr & MUSB_RXCSR_RXPKTRDY)) + return 0; + + count = readw((void *)OMAP34XX_USB_RXCOUNT(BULK_ENDPOINT)); + for (cntr = 0; cntr < count; cntr++) + *buffer++ = readb(bulk_fifo); + + /* Clear the RXPKTRDY bit */ + rxcsr = readw(reg); + rxcsr &= ~MUSB_RXCSR_RXPKTRDY; + writew(rxcsr, reg); + + return count; +} + +static unsigned char usb_outbuffer[64]; + +static void usb_msg(unsigned int cmd, const char *msg) +{ + unsigned char *p_char = usb_outbuffer; + + *(int *)p_char = cmd; + + p_char += sizeof(cmd); + + if (msg) { + while (*msg) + *p_char++= *msg++; + *p_char++= 0; + } + + usb_send(usb_outbuffer, p_char - usb_outbuffer); +} + +static void usb_code(unsigned int cmd, u32 code) +{ + unsigned int *p_int = (unsigned int *)usb_outbuffer; + + *p_int++ = cmd; + *p_int++ = code; + usb_send (usb_outbuffer, ((unsigned char *) p_int) - usb_outbuffer); +} + +void *omap3_xload_boot_usb(void) +{ + int res; + void *buf; + u32 *buf32; + u32 total; + void *addr; + u32 bytes; + int size; + int cntr; + void *fn; + u8 __buf[512]; + + buf32 = buf = __buf; + + usb_msg (USBLOAD_CMD_FILE_REQ, "file req"); + for (cntr = 0; cntr < 10000000; cntr++) { + size = usb_recv(buf); + if (!size) + continue; + + switch (buf32[0]) { + case USBLOAD_CMD_FILE: + pr_debug ("USBLOAD_CMD_FILE total = %d size = 0x%x addr = 0x%x\n", + res, buf32[1], buf32[2]); + total = buf32[1]; /* get size and address */ + addr = (void *)buf32[2]; + usb_code(USBLOAD_CMD_ECHO_SZ, total); + + bytes = 0; + + while (bytes < total) { + size = usb_recv(buf); + memcpy(addr, buf, size); + addr += size; + bytes += size; + } + + usb_code(USBLOAD_CMD_REPORT_SZ, total); /* tell him we got this many bytes */ + usb_msg (USBLOAD_CMD_FILE_REQ, "file req"); /* see if they have another file for us */ + break; + case USBLOAD_CMD_JUMP: + pr_debug("USBLOAD_CMD_JUMP total = %d addr = 0x%x val = 0x%x\n", + res, buf32[0], buf32[1]); + fn = (void *)buf32[1]; + goto out; + default: + break; + } + } + + fn = NULL; +out: + + return fn; +} diff --git a/arch/arm/mach-omap/xload.c b/arch/arm/mach-omap/xload.c index 85c9120..4a0714e 100644 --- a/arch/arm/mach-omap/xload.c +++ b/arch/arm/mach-omap/xload.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -284,13 +285,16 @@ func = omap_xload_boot_mmc(); break; case BOOTSOURCE_USB: - if (IS_ENABLED(CONFIG_FS_OMAP4_USBBOOT)) { + if (IS_ENABLED(CONFIG_OMAP3_USBBOOT) && cpu_is_omap3()) { + printf("booting from USB\n"); + func = omap3_xload_boot_usb(); + } else if (IS_ENABLED(CONFIG_FS_OMAP4_USBBOOT)) { printf("booting from USB\n"); func = omap4_xload_boot_usb(); - break; } else { printf("booting from USB not enabled\n"); } + break; case BOOTSOURCE_NAND: printf("booting from NAND\n"); func = omap_xload_boot_nand(barebox_part->nand_offset, diff --git a/scripts/Makefile b/scripts/Makefile index 74c2213..a3f6222 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -24,6 +24,9 @@ HOSTCFLAGS_mxs-usb-loader.o = `pkg-config --cflags libusb-1.0` HOSTLOADLIBES_mxs-usb-loader = `pkg-config --libs libusb-1.0` hostprogs-$(CONFIG_ARCH_MXS_USBLOADER) += mxs-usb-loader +HOSTCFLAGS_omap3-usb-loader.o = `pkg-config --cflags libusb-1.0` +HOSTLOADLIBES_omap3-usb-loader = `pkg-config --libs libusb-1.0` +hostprogs-$(CONFIG_OMAP3_USB_LOADER) += omap3-usb-loader subdir-y += mod subdir-$(CONFIG_OMAP4_USBBOOT) += omap4_usbboot diff --git a/scripts/omap3-usb-loader.c b/scripts/omap3-usb-loader.c new file mode 100644 index 0000000..edf6043 --- /dev/null +++ b/scripts/omap3-usb-loader.c @@ -0,0 +1,921 @@ +/* + * OMAP Loader, a USB uploader application targeted at OMAP3 processors + * Copyright (C) 2008 Martin Mueller + * Copyright (C) 2014 Grant Hernandez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Reasons for the name change: this is a complete rewrite of + * the unversioned omap3_usbload so to lower ambiguity the name was changed. + * The GPLv2 license specifies rewrites as derived work. + */ +#define PROG_NAME "OMAP Loader" +#define VERSION "1.0.0" + +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define OMAP_IS_BIG_ENDIAN +#endif + +#ifdef OMAP_IS_BIG_ENDIAN +#include +#endif + +#include /* for usleep and friends */ +#include +#include +#include /* for basename */ + +#include /* the main event */ + +/* Device specific defines (OMAP) + * Primary source: http://www.ti.com/lit/pdf/sprugn4 + * Section 26.4.5 "Peripheral Booting" + */ +#define OMAP_BASE_ADDRESS 0x40200000 +#define OMAP_PERIPH_BOOT 0xF0030002 +#define OMAP_VENDOR_ID 0x0451 +#define OMAP_PRODUCT_ID 0xD00E +/* TODO: dynamically discover these endpoints */ +#define OMAP_USB_BULK_IN 0x81 +#define OMAP_USB_BULK_OUT 0x01 +#define OMAP_ASIC_ID_LEN 69 + +#ifdef OMAP_IS_BIG_ENDIAN +#define cpu_to_le32(v) (((v & 0xff) << 24) | ((v & 0xff00) << 8) | \ + ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24)) +#define le32_to_cpu(v) cpu_to_le32(v) +#else +#define cpu_to_le32(v) (v) +#define le32_to_cpu(v) (v) +#endif + +/* + * taken from x-loader/drivers/usb/usb.c + * All credit to Martin Mueller + */ +#define PACK4(a,b,c,d) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)) +#define USBLOAD_CMD_FILE PACK4('U','S','B','s') /* file size request */ +#define USBLOAD_CMD_FILE_REQ PACK4('U','S','B','f') /* file size resp */ +#define USBLOAD_CMD_JUMP PACK4('U','S','B','j') /* execute code here */ +#define USBLOAD_CMD_ECHO_SZ PACK4('U','S','B','n') /* file size confirm to */ +#define USBLOAD_CMD_REPORT_SZ PACK4('U','S','B','o') /* confirm full file */ +#define USBLOAD_CMD_MESSAGE PACK4('U','S','B','m') /* debug message */ + +/* USB transfer characteristics */ +#define USB_MAX_WAIT 5000 +#define USB_TIMEOUT 1000 +#define USB_MAX_TIMEOUTS (USB_MAX_WAIT/USB_TIMEOUT) + +/* stores the data and attributes of a file to upload in to memory */ +struct file_upload { + size_t size; + void *data; + uint32_t addr; + char *path; + char *basename; +}; + +/* stores all of the arguments read in by getopt in main() */ +struct arg_state { + struct file_upload **files; + int numfiles; + uint32_t jumptarget; + uint16_t vendor, product; +}; + +static int g_verbose = 0; + +static void log_error(char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + fprintf(stdout, "[-] "); + vfprintf(stdout, fmt, va); + va_end(va); +} + +static void log_info(char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + fprintf(stdout, "[+] "); + vfprintf(stdout, fmt, va); + va_end(va); +} + +static bool omap_usb_read(libusb_device_handle *handle, unsigned char *data, + int length, int *actuallength) +{ + int ret = 0; + int iter = 0; + int sizeleft = length; + + if (!actuallength) + return false; + + while (sizeleft > 0) { + int actualread = 0; + int readamt = sizeleft; + + ret = libusb_bulk_transfer(handle, OMAP_USB_BULK_IN, data + iter, + readamt, &actualread, USB_TIMEOUT); + + if (ret == LIBUSB_ERROR_TIMEOUT) { + sizeleft -= actualread; + iter += actualread; + + /* we got some data, lets cut our losses and stop here */ + if (iter > 0) + break; + + log_error("device timed out while transfering in %d bytes (got %d)\n", + length, iter); + + return false; + } else if (ret == LIBUSB_SUCCESS) { + /* we cant trust actualRead on anything but a timeout or success */ + sizeleft -= actualread; + iter += actualread; + + /* stop at the first sign of data */ + if (iter > 0) + break; + } else { + log_error("fatal transfer error (BULK_IN) for %d bytes (got %d): %s\n", + length, iter, libusb_error_name(ret)); + return false; + } + } + + *actuallength = iter; + + return true; +} + +static bool omap_usb_write(libusb_device_handle *handle, void *data, int length) +{ + int ret = 0; + int numtimeouts = 0; + int iter = 0; + int sizeleft = length; + + while (sizeleft > 0) { + int actualwrite = 0; + int writeamt = sizeleft > 512 ? 512 : sizeleft; + + ret = libusb_bulk_transfer(handle, OMAP_USB_BULK_OUT, data + iter, + writeamt, &actualwrite, USB_TIMEOUT); + + if (ret == LIBUSB_ERROR_TIMEOUT) { + numtimeouts++; + sizeleft -= actualwrite; + iter += actualwrite; + + /* build in some reliablity */ + if (numtimeouts > USB_MAX_TIMEOUTS) { + log_error("device timed out while transfering out %d bytes (%d made it)\n", + length, iter); + return false; + } + } else if (ret == LIBUSB_SUCCESS) { + /* we cant trust actualWrite on anything but a timeout or success */ + sizeleft -= actualwrite; + iter += actualwrite; + } else { + log_error("fatal transfer error (BULK_OUT) for %d bytes (%d made it): %s\n", + length, iter, libusb_error_name(ret)); + return false; + } + } + + return true; +} + +static unsigned char *omap_usb_get_string(libusb_device_handle *handle, uint8_t idx) +{ + unsigned char *data = NULL; + int len = 0; + int ret = 0; + + if (!handle) + return NULL; + + while (true) { + if (!len || ret < 0) { + len += 256; + data = realloc(data, len); + + if (!data) + return NULL; + } + + ret = libusb_get_string_descriptor_ascii(handle, idx, data, len); + + /* we can still recover... */ + if (ret < 0) { + if (ret == LIBUSB_ERROR_INVALID_PARAM) + continue; /* try again with an increased size */ + + log_error("failed to lookup string index %hhu: %s\n", + idx, libusb_error_name(ret)); + + /* unrecoverable */ + free(data); + return NULL; + } else { + /* we got something! */ + break; + } + } + + return data; +} + +uint16_t omap_products[] = { + 0xd009, + 0xd00f, +}; + +static libusb_device_handle *omap_usb_open(libusb_context *ctx, uint16_t vendor, uint16_t product) +{ + libusb_device **devlist; + libusb_device_handle *handle; + struct libusb_device_descriptor desc; + ssize_t count, i; + int ret; + + log_info("scanning for USB device matching %04hx:%04hx...\n", + vendor, product); + + while (1) { + if ((count = libusb_get_device_list(ctx, &devlist)) < 0) { + log_error("failed to gather USB device list: %s\n", + libusb_error_name(count)); + return NULL; + } + + for (i = 0; i < count; i++) { + ret = libusb_get_device_descriptor(devlist[i], &desc); + if (ret < 0) { + log_error("failed to get USB device descriptor: %s\n", + libusb_error_name(ret)); + libusb_free_device_list(devlist, 1); + return NULL; + } + + if (desc.idVendor != vendor) + continue; + + if (product) { + if (desc.idProduct != product) + continue; + goto found; + } + + for (i = 0; i < sizeof(omap_products) / sizeof(uint16_t); i++) + if (desc.idProduct == omap_products[i]) { + product = desc.idProduct; + goto found; + } + } + + libusb_free_device_list(devlist, 1); + + /* nothing found yet. have a 10ms nap */ + usleep(10000); + } +found: + ret = libusb_open(devlist[i], &handle); + if (ret < 0) { + log_error("failed to open USB device %04hx:%04hx: %s\n", + vendor, product, libusb_error_name(ret)); + libusb_free_device_list(devlist, 1); + return NULL; + } + + ret = libusb_claim_interface(handle, 0); + if (ret) { + printf("Claim failed\n"); + return NULL; + } + + /* grab the manufacturer and product strings for printing */ + unsigned char *mfgstr = omap_usb_get_string(handle, desc.iManufacturer); + unsigned char *prodstr = omap_usb_get_string(handle, desc.iProduct); + + log_info("successfully opened %04hx:%04hx (", vendor, product); + + if (mfgstr) { + fprintf(stdout, prodstr ? "%s " : "%s", mfgstr); + free(mfgstr); + } + + if (prodstr) { + fprintf(stdout, "%s", prodstr); + free(prodstr); + } + + fprintf(stdout, ")\n"); + + return handle; +} + +static unsigned char *read_file(char *path, size_t *readamt) +{ + FILE *fp = fopen(path, "rb"); + + if (!fp) { + log_error("failed to open file \'%s\': %s\n", path, + strerror(errno)); + return NULL; + } + + unsigned char *data = NULL; + size_t allocsize = 0; + size_t iter = 0; + + while (1) { + if (iter >= iter) { + allocsize += 1024; + data = realloc(data, allocsize); + if (!data) + return NULL; + } + + size_t readsize = allocsize - iter; + size_t ret = fread(data + iter, sizeof (unsigned char), readsize, fp); + + iter += ret; + + if (ret != readsize) { + if (feof(fp)) { + break; + } else if (ferror(fp)) { + log_error("error file reading file \'%s\': %s\n", + path, strerror(errno)); + free(data); + return NULL; + } + } + } + + /* trim the allocation down to size */ + data = realloc(data, iter); + *readamt = iter; + + return data; +} + +static int transfer_first_stage(libusb_device_handle * handle, struct arg_state *args) +{ + unsigned char *buffer = NULL; + uint32_t cmd = 0; + uint32_t filelen = 0; + int bufsize = 0x200; + int transLen = 0; + int i; + void *data; + uint32_t *dbuf; + + struct file_upload *file = args->files[0]; + + /* TODO determine buffer size based on endpoint */ + buffer = calloc(bufsize, sizeof (unsigned char)); + filelen = cpu_to_le32(file->size); + + data = file->data; + dbuf = data; + + if (le32toh(dbuf[5]) == 0x45534843) { + int chsettingssize = 512 + 2 * sizeof(uint32_t); + + log_info("CHSETTINGS image detected. Skipping header\n"); + + data += chsettingssize; + filelen -= chsettingssize; + } + + /* read the ASIC ID */ + if (!omap_usb_read(handle, buffer, bufsize, &transLen)) { + log_error("failed to read ASIC ID from USB connection. " + "Check your USB device!\n"); + goto fail; + } + + if (transLen != OMAP_ASIC_ID_LEN) { + log_error("got some ASIC ID, but it's not the right length, %d " + "(expected %d)\n", transLen, OMAP_ASIC_ID_LEN); + goto fail; + } + + /* optionally, print some ASIC ID info */ + if (g_verbose) { + char *fields[] = + { "Num Subblocks", "Device ID Info", "Reserved", + "Ident Data", "Reserved", "CRC (4 bytes)" + }; + int fieldLen[] = { 1, 7, 4, 23, 23, 11 }; + int field = 0; + int nextSep = 0; + + log_info("got ASIC ID - "); + + for (i = 0; i < transLen; i++) { + if (i == nextSep) { + fprintf(stdout, "%s%s ", + (field > 0) ? ", " : "", fields[field]); + nextSep += fieldLen[field]; + field++; + + fprintf(stdout, "["); + } + + fprintf(stdout, "%02x", buffer[i]); + + if (i + 1 == nextSep) + fprintf(stdout, "]"); + } + + fprintf(stdout, "\n"); + } + + /* send the peripheral boot command */ + cmd = cpu_to_le32(OMAP_PERIPH_BOOT); + + if (!omap_usb_write(handle, (unsigned char *) &cmd, sizeof (cmd))) { + log_error("failed to send the peripheral boot command 0x%08x\n", + OMAP_PERIPH_BOOT); + goto fail; + } + + /* send the length of the first file (little endian) */ + if (!omap_usb_write + (handle, (unsigned char *) &filelen, sizeof (filelen))) { + log_error("failed to length specifier of %u to OMAP BootROM\n", + filelen); + goto fail; + } + + /* send the file! */ + if (!omap_usb_write(handle, data, filelen)) { + log_error("failed to send file \'%s\' (size %u)\n", + file->basename, filelen); + goto fail; + } + + free(buffer); + return 1; + + fail: + free(buffer); + return 0; +} + +static int transfer_other_files(libusb_device_handle *handle, struct arg_state *args) +{ + uint32_t *buffer = NULL; + int bufsize = 128 * sizeof (*buffer); + int numfailures = 0; /* build in some reliablity */ + int maxfailures = 3; + int transLen = 0; + int curfile = 1; /* skip the first file */ + size_t len; + + buffer = calloc(bufsize, sizeof(unsigned char)); + + /* handle the state machine for the X-Loader */ + while (curfile < args->numfiles) { + uint32_t opcode = 0; + uint8_t *extra = NULL; + struct file_upload *f = args->files[curfile]; + + /* read the opcode from xloader ID */ + if (!omap_usb_read + (handle, (unsigned char *) buffer, bufsize, &transLen)) { + numfailures++; + + if (numfailures >= maxfailures) { + log_error("failed to read command from X-Loader\n"); + goto fail; + } + + /* sleep a bit */ + usleep(2000 * 1000); /* 2s */ + continue; /* try the opcode read again */ + } + + if (transLen < 8) { + log_error("failed to recieve enough data for the opcode\n"); + goto fail; + } + + /* extract the opcode and extra data pointer */ + opcode = le32_to_cpu(buffer[0]); + extra = (uint8_t *)buffer; + + switch (opcode) { + case USBLOAD_CMD_FILE_REQ: + /* X-loader is requesting a file to be sent */ + /* send the opcode, size, and addr */ + buffer[0] = cpu_to_le32(USBLOAD_CMD_FILE); + buffer[1] = cpu_to_le32(f->size); + buffer[2] = cpu_to_le32(f->addr); + + if (!omap_usb_write(handle, (unsigned char *)buffer, sizeof(*buffer) * 3)) { + log_error("failed to send load file command to the X-loader\n"); + goto fail; + } + + if (g_verbose) { + log_info("uploading \'%s\' (size %zu) to 0x%08x\n", + f->basename, f->size, f->addr); + } + + break; + case USBLOAD_CMD_ECHO_SZ: + /* X-loader confirms the size to recieve */ + if (buffer[1] != f->size) { + log_error + ("X-loader failed to recieve the right file size for " + "file \'%s\' (got %u, expected %zu)\n", + f->basename, buffer[1], f->size); + goto fail; + } + + /* upload the raw file data */ + if (!omap_usb_write(handle, f->data, f->size)) { + log_error + ("failed to send file \'%s\' to the X-loader\n", + f->basename); + goto fail; + } + + break; + case USBLOAD_CMD_REPORT_SZ: + /* X-loader confirms the amount of data it recieved */ + if (buffer[1] != f->size) { + log_error + ("X-loader failed to recieve the right amount of data for " + "file \'%s\' (got %u, expected %zu)\n", + f->basename, buffer[1], f->size); + goto fail; + } + + curfile++; /* move on to the next file */ + break; + case USBLOAD_CMD_MESSAGE: + /* X-loader debug message */ + len = strlen((char *)extra); + + if (len > (bufsize - sizeof (opcode) - 1)) + log_error("X-loader debug message not NUL terminated (size %zu)\n", + len); + else + fprintf(stdout, "X-loader Debug: %s\n", extra); + break; + default: + log_error("unknown X-Loader opcode 0x%08X (%c%c%c%c)\n", + opcode, extra[0], extra[1], extra[2], + extra[3]); + goto fail; + } + } + + /* we're done uploading files to X-loader send the jump command */ + buffer[0] = cpu_to_le32(USBLOAD_CMD_JUMP); + buffer[1] = cpu_to_le32(args->jumptarget); + + if (!omap_usb_write + (handle, (unsigned char *) buffer, sizeof (*buffer) * 2)) { + log_error + ("failed to send the final jump command to the X-loader. " + "Target was 0x%08x\n", args->jumptarget); + goto fail; + } + + if (g_verbose) + log_info("jumping to address 0x%08x\n", args->jumptarget); + + free(buffer); + return 1; + + fail: + free(buffer); + return 0; +} + +static int process_args(struct arg_state *args) +{ + int i; + + /* For each file, load it in to memory + * TODO: defer this until transfer time (save memory and pipeline IO) + */ + + for (i = 0; i < args->numfiles; i++) { + struct file_upload *f = args->files[i]; + + f->data = read_file(f->path, &f->size); + + if (!f->data) { + return 1; + } + } + + if (g_verbose > 0) { + for (i = 0; i < args->numfiles; i++) { + struct file_upload *f = args->files[i]; + + printf("File \'%s\' at 0x%08x, size %zu\n", + f->basename, f->addr, f->size); + } + } + + libusb_context *ctx; + libusb_device_handle *dev; + int ret; + + if ((ret = libusb_init(&ctx)) < 0) { + log_error("failed to initialize libusb context: %s\n", + libusb_error_name(ret)); + return ret; + } + + dev = omap_usb_open(ctx, args->vendor, args->product); + + if (!dev) { + libusb_exit(ctx); + return 1; + } + + /* Communicate with the TI BootROM directly + * - retrieve ASIC ID + * - start peripheral boot + * - upload first file + * - execute first file + */ + if (!transfer_first_stage(dev, args)) { + log_error("failed to transfer the first stage file \'%s\'\n", + args->files[0]->basename); + goto fail; + } + + /* Note: this is a race between the target's processor getting X-loader + * running and our processor. If we fail to communicate with the X-loader, + * it's possible that it hasn't been fully initialized. I'm not going to put + * some stupid, arbitrary sleep value here. The transfer_other_files function + * should be robust enough to handle some errors. + */ + + /* If we are passed one file, assume that the user just wants to + * upload some initial code with no X-loader chaining + */ + if (args->numfiles > 1) { + if (!transfer_other_files(dev, args)) { + log_error + ("failed to transfer the additional files in to memory\n"); + goto fail; + } + } + + log_info("successfully transfered %d %s\n", args->numfiles, + (args->numfiles > 1) ? "files" : "file"); + + /* safely close our USB handle and context */ + libusb_close(dev); + libusb_exit(ctx); + return 0; + +fail: + libusb_close(dev); + libusb_exit(ctx); + + return 1; +} + +/* getopt configuration */ +static int do_version = 0; +static const char *const short_opt = "f:a:j:i:p:vh"; +static const struct option long_opt[] = { + {"file", 1, NULL, 'f'}, + {"addr", 1, NULL, 'a'}, + {"jump", 1, NULL, 'j'}, + {"vendor", 1, NULL, 'i'}, + {"product", 1, NULL, 'p'}, + {"verbose", 0, NULL, 'v'}, + {"help", 0, NULL, 'h'}, + {"version", 0, &do_version, 1}, + {NULL, 0, NULL, 0} +}; + +static void usage(char *exe) +{ + printf( +"Usage: %s [options] -f first-stage [-f file -a addr]...\n" +"Options:\n" +" -f, --file Provide the filename of a binary file to be\n" +" uploaded. The first file specified is uploaded to\n" +" the fixed address 0x%08x as defined by the manual.\n" +" Additional files must be followed by an address\n" +" argument (-a).\n" +" -a, --addr The address to load the prior file at.\n" +" -j, --jump Specify the address to jump to after loading all\n" +" of the files in to memory.\n" +" -i, --vendor Override the default vendor ID to poll for\n" +" (default 0x%04x).\n" +" -p, --product Poll for specific product id. Default: All known OMAP chips\n" +" -h, --help Display this message.\n" +" -v, --verbose Enable verbose output.\n" +"\n" +"Description:\n" +" %s's basic usage is to upload an arbitrary file in to the memory\n" +" of a TI OMAP3 compatible processor. This program directly\n" +" communicates with the TI BootROM in order to upload a first stage\n" +" payload, in most cases, TI's X-Loader. Using a compatible X-Loader\n" +" will enable the upload of any file to any part in device memory.\n" +"\n" +"Examples:\n" +" Uploading a compatible X-Loader, U-Boot, Kernel, and RAMDisk, then jumping\n" +" to the U-Boot image for further bootloading:\n" +" %s -f x-load.bin -f u-boot.bin -a 0x80200000 -f uImage -a 0x80800000 \\\n" +" -f uRamdisk -a 0x81000000 -j 0x80200000\n" +" Uploading arbitrary code to be executed (doesn't have to be X-loader):\n" +" %s -f exec_me.bin\n" +" Trying to debug an upload issue using verbose output:\n" +" %s -v -f exec_me.bin -f file1.bin -a 0xdeadbeef -j 0xabad1dea\n" +"\n" +"Authors:\n" +" Grant Hernandez - rewrite of omap3_usbload to\n" +" use the newer libusb 1.0\n" +" Martin Mueller - initial code (omap3_usbload)\n" +" and X-Loader patch\n", + exe, OMAP_BASE_ADDRESS, OMAP_VENDOR_ID, PROG_NAME, exe, exe, exe + ); +} + +static void license(void) +{ + printf( +"Copyright (C) 2008 Martin Mueller \n" +"Copyright (C) 2014 Grant Hernandez \n" +"License GPLv2: GNU GPL version 2 or later .\n" +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n" + ); +} + +int main(int argc, char *argv[]) +{ + int opt; + bool gotfile = false; + bool gotjump = false; + int filecount = 0; + char *exe = NULL; + + /* temporary local file object */ + struct file_upload file; + /* total arg state */ + struct arg_state *args = calloc(1, sizeof (*args)); + + if (argc < 1) { + log_error("invalid arguments (no argv[0])\n"); + return 1; + } + + exe = argv[0]; + + fprintf(stdout, "%s %s\n", PROG_NAME, VERSION); + + /* set the default vendor and product */ + args->vendor = OMAP_VENDOR_ID; + args->product = 0; + + while ((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) { + switch (opt) { + case 0: + if (do_version) { + license(); + return 0; + } + break; + case 'f': + if (gotfile) { + log_error("missing address argument (-a) for file \'%s\'\n", + file.path); + usage(exe); + return 1; + } + + file.path = strdup(optarg); + + /* necessary to be sure that we own all the memory + and that the path input can be modified */ + char *tmpPath = strdup(file.path); + file.basename = strdup(basename(tmpPath)); + free(tmpPath); + + filecount++; + + /* the first file gets uploaded to a fixed address + as specified by the technical reference manual */ + if (filecount == 1) { + file.addr = OMAP_BASE_ADDRESS; + + /* commit the file object with the processor specified base address */ + args->files = realloc(args->files, filecount); + args->numfiles = filecount; + args->files[filecount - 1] = malloc(sizeof (file)); + memcpy(args->files[filecount - 1], &file, sizeof (file)); + } else { + /* commit only after an address is specified */ + gotfile = true; + } + break; + case 'a': + if (!gotfile) { + log_error + ("missing file argument (-f) before address \'%s\'\n", + optarg); + usage(exe); + return 1; + } + + /* passing 0 to strtoul enables detection of the 0x prefix with + base-10 fallback if missing */ + file.addr = strtoul(optarg, NULL, 0); + + /* commit the file object */ + args->files = realloc(args->files, filecount); + args->numfiles = filecount; + args->files[filecount - 1] = malloc(sizeof(file)); + memcpy(args->files[filecount - 1], &file, sizeof(file)); + + gotfile = false; + break; + case 'j': + args->jumptarget = strtoul(optarg, NULL, 0); + gotjump = true; + break; + case 'i': + args->vendor = (uint16_t)strtoul(optarg, NULL, 0); + break; + case 'p': + args->product = (uint16_t)strtoul(optarg, NULL, 0); + break; + case 'v': + g_verbose++; + break; + case 'h': + usage(exe); + return 0; + default: + usage(exe); + return 1; + } + } + + if (gotfile) { + log_error("got file \'%s\', but no address!\n", file.path); + usage(exe); + return 1; + } + + if (args->numfiles <= 0) { + log_error("at least one file needs to be specified\n"); + usage(exe); + return 1; + } + + if (args->numfiles == 1 && gotjump) { + log_info + ("WARNING: jump target 0x%08x specified, but will never be taken " + "(more than one file required)\n", args->jumptarget); + } else if (args->numfiles > 1 && !gotjump) { + log_info + ("WARNING: no jump target specified. Defaulting to the first " + "file's (\'%s\') address 0x%08x\n", + args->files[1]->basename, args->files[1]->addr); + args->jumptarget = args->files[1]->addr; + } + + return process_args(args); +}