9
0
Fork 0

ARM: OMAP: Add omap3 USB loader tool

The OMAP3 supports uploading the first stage bootloader via USB.
The ROM leaves the MUSB controller enabled and it can then be used
to upload a 2nd stage image. This patch adds the omap3-usb-loader tool
and the necessary barebox support to upload the 2nd stage image.

The omap usb loader tool is downloaded from https://github.com/grant-h/omap_loader
and changed to also accept CHSETTINGS images.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Sascha Hauer 2015-06-24 14:25:10 +02:00
parent 302076b017
commit 1d7c8ec143
7 changed files with 1134 additions and 2 deletions

View File

@ -120,6 +120,21 @@ config OMAP4_USBBOOT
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

View File

@ -31,6 +31,8 @@ obj-$(CONFIG_OMAP_GPMC) += gpmc.o devices-gpmc-nand.o
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

View File

@ -29,4 +29,6 @@ void __noreturn omap3_reset_cpu(unsigned long addr);
int omap3_init(void);
int omap3_devices_init(void);
void *omap3_xload_boot_usb(void);
#endif /* __MACH_OMAP3_GENERIC_H */

View File

@ -0,0 +1,185 @@
/*
* Copyright (c) 2015 Sascha Hauer <s.hauer@pengutronix.de>, 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 <common.h>
#include <io.h>
#include <malloc.h>
#include <mach/omap3-silicon.h>
#include <mach/omap3-generic.h>
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;
}

View File

@ -14,6 +14,7 @@
#include <xymodem.h>
#include <mach/generic.h>
#include <mach/am33xx-generic.h>
#include <mach/omap3-generic.h>
#include <net.h>
#include <environment.h>
#include <dhcp.h>
@ -284,13 +285,16 @@ static __noreturn int omap_xload(void)
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,

View File

@ -24,6 +24,9 @@ HOSTLOADLIBES_mxsimage = `pkg-config --libs openssl`
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

921
scripts/omap3-usb-loader.c Normal file
View File

@ -0,0 +1,921 @@
/*
* OMAP Loader, a USB uploader application targeted at OMAP3 processors
* Copyright (C) 2008 Martin Mueller <martinmm@pfump.org>
* Copyright (C) 2014 Grant Hernandez <grant.h.hernandez@gmail.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <stdbool.h>
/*
* 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 <arpa/inet.h>
#endif
#include <unistd.h> /* for usleep and friends */
#include <getopt.h>
#include <errno.h>
#include <libgen.h> /* for basename */
#include <libusb-1.0/libusb.h> /* 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 <grant.h.hernandez@gmail.com> - rewrite of omap3_usbload to\n"
" use the newer libusb 1.0\n"
" Martin Mueller <martinmm@pfump.org> - 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 <martinmm@pfump.org>\n"
"Copyright (C) 2014 Grant Hernandez <grant.h.hernandez@gmail.com>\n"
"License GPLv2: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\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);
}