/* * 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 #include #include #include #include #include "usb.h" #define USBBOOT_FS_MAGIC 0x5562464D #define USBBOOT_FS_CMD_OPEN 0x46530000 #define USBBOOT_FS_CMD_CLOSE 0x46530001 #define USBBOOT_FS_CMD_READ 0x46530002 #define USBBOOT_FS_CMD_END 0x4653FFFF #define MAX_OPEN_FILES 128 #define RESET 0 #define BRIGHT 1 #define WHITE 8 #define RED 1 #define BLACK 0 #define FORMAT "%c[%d;%d;%dm" #define TARGET_FORMAT 0x1B, BRIGHT, RED+30, BLACK+40 #define HOST_FORMAT 0x1B, RESET, WHITE+30, BLACK+40 #define host_print(fmt, arg...) printf(FORMAT fmt FORMAT, \ HOST_FORMAT, ##arg, TARGET_FORMAT) struct thread_vars { struct usb_handle *usb; int hide; struct termios to; }; void *listenerTask(void *argument) { struct thread_vars *vars = argument; int c; for (;;) { c = getchar(); if (c == EOF) return NULL; while (vars->hide) usleep(10000); if (usb_write(vars->usb, &c, 4) != 4) { host_print("could not send '%c' to target\n", c); tcsetattr(STDIN_FILENO, TCSANOW, &vars->to); exit(1); } } return NULL; } int read_asic_id(struct usb_handle *usb) { #define LINEWIDTH 16 const uint32_t msg_getid = 0xF0030003; int i, j, k, ret; uint8_t id[81]; char line[LINEWIDTH*3+8]; printf("reading ASIC ID\n"); memset(id , 0xee, sizeof(id)); if (usb_write(usb, &msg_getid, sizeof(msg_getid)) != sizeof(msg_getid) ) { printf("Could not send msg_getid request\n"); return -1; } if (usb_read(usb, id, sizeof(id)) != sizeof(id)) { printf("Could not read msg_getid answer\n"); return -1; } for (i = 0; i < sizeof(id); i += LINEWIDTH) { sprintf(line, "%02X: ", i); for (j = 0; j < LINEWIDTH && j < sizeof(id)-i; j++) sprintf(line+4+j*3, "%02X ", id[i+j]); line[4+j*3+0] = '\n'; line[4+j*3+1] = 0; printf(line); } ret = 0; for (i = 1, j = 0; i < sizeof(id) && j < id[0]; i += 2+id[i+1], j++) { if (i+2+id[i+1] > sizeof(id)) { printf("Truncated subblock\n"); ret++; continue; } switch (id[i]) { case 0x01: /* ID subblock */ if (id[i+1] != 0x05) { printf("Unexpected ID subblock size\n"); ret++; continue; } if (id[i+2] != 0x01) printf("Unexpected fixed value\n"); k = (id[i+3]<<8) | id[i+4]; switch (k) { case 0x4440: printf("OMAP 4460 Device\n"); break; default: printf("Unknown Device\n"); break; } switch (id[i+5]) { case 0x07: printf("CH enabled (read from eFuse)\n"); break; case 0x17: printf("CH disabled (read from eFuse)\n"); break; default: printf("Unknown CH setting\n"); break; } printf("Rom version: %hhu\n", id[i+6]); break; case 0x15: /* Checksum subblock */ if (id[i+1] != 0x09) { printf("Unexpected Checksum subblock size\n"); ret++; continue; } if (id[i+2] != 0x01) printf("Unexpected fixed value\n"); k = (id[i+3]<<24) | (id[i+4]<<16) | (id[i+5]<<8) | id[i+6]; printf("Rom CRC: 0x%08X\n", k); k = (id[i+7]<<24) | (id[i+8]<<16) | (id[i+9]<<8) | id[i+10]; switch (k) { case 0: printf("A GP device\n"); break; default: printf("Unknown device\n"); break; } break; } } if (i != sizeof(id) || j != id[0]) { printf("Unexpected ASIC ID structure size.\n"); ret++; } return ret; } struct file_data { size_t size; void *data; }; int process_file( struct usb_handle *usb, const char *rootfs, struct file_data *fd_vector) { uint32_t i, j, pos, size; struct stat s; int fd, ret; char fname[256]; if (usb_read(usb, &i, 4) != 4) { host_print("USB error\n"); exit(1); } ret = 0; switch (i) { case USBBOOT_FS_CMD_OPEN: for (j = 0; rootfs[j]; j++) fname[j] = rootfs[j]; for (;; j++) { if (usb_read(usb, &i, 4) != 4) { host_print("USB error\n"); exit(1); } if (i == USBBOOT_FS_CMD_END) { fname[j] = 0; break; } else if (i > 0xFF) { host_print("Error in filename\n"); ret++; fname[j] = 0; break; } else fname[j] = i; } for (i = 0; i < MAX_OPEN_FILES && fd_vector[i].data; i++) ; if (i >= MAX_OPEN_FILES) { host_print("MAX_OPEN_FILES exceeded\n"); ret++; goto open_error_1; } fd = open(fname, O_RDONLY); if (fd < 0) { host_print("cannot open '%s'\n", fname); ret++; goto open_error_1; } if (fstat(fd, &s)) { host_print("cannot stat '%s'\n", fname); ret++; goto open_error_2; } fd_vector[i].data = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (fd_vector[i].data == MAP_FAILED) { host_print("cannot mmap '%s'\n", fname); ret++; goto open_error_2; } close(fd); fd_vector[i].size = size = s.st_size; fd = i; goto open_ok; open_error_2: close(fd); open_error_1: fd_vector[i].size = size = 0; fd_vector[i].data = NULL; fd = -1; open_ok: if (usb_write(usb, &fd, 4) != 4 || usb_write(usb, &size, 4) != 4 ) { host_print("could not send file size to target\n"); exit(1); } break; case USBBOOT_FS_CMD_CLOSE: if (usb_read(usb, &i, 4) != 4) { host_print("USB error\n"); exit(1); } if (i >= MAX_OPEN_FILES || !fd_vector[i].data) { host_print("invalid close index\n"); ret++; break; } if (usb_read(usb, &j, 4) != 4) { host_print("USB error\n"); exit(1); } if (j != USBBOOT_FS_CMD_END) { host_print("invalid close\n"); ret++; break; } munmap(fd_vector[i].data, fd_vector[i].size); fd_vector[i].data = NULL; break; case USBBOOT_FS_CMD_READ: if (usb_read(usb, &i, 4) != 4) { host_print("USB error\n"); exit(1); } if (i >= MAX_OPEN_FILES || !fd_vector[i].data) { host_print("invalid read index\n"); ret++; break; } if (usb_read(usb, &pos, 4) != 4) { host_print("USB error\n"); exit(1); } if (pos >= fd_vector[i].size) { host_print("invalid read pos\n"); ret++; break; } if (usb_read(usb, &size, 4) != 4) { host_print("USB error\n"); exit(1); } if (pos+size > fd_vector[i].size) { host_print("invalid read size\n"); ret++; break; } if (usb_read(usb, &j, 4) != 4) { host_print("USB error\n"); exit(1); } if (j != USBBOOT_FS_CMD_END) { host_print("invalid read\n"); ret++; break; } if (usb_write(usb, fd_vector[i].data+pos, size) != size) { host_print("could not send file to target\n"); exit(1); } break; case USBBOOT_FS_CMD_END: default: host_print("Unknown filesystem command\n"); ret++; break; } return ret; } int usb_boot( struct usb_handle *usb, void *data, unsigned sz, const char *rootfs) { const uint32_t msg_boot = 0xF0030002; uint32_t msg_size = sz; int i; pthread_t thread; struct thread_vars vars; struct termios tn; struct file_data fd_vector[MAX_OPEN_FILES]; read_asic_id(usb); printf("sending xload to target...\n"); usb_write(usb, &msg_boot, sizeof(msg_boot)); usb_write(usb, &msg_size, sizeof(msg_size)); usb_write(usb, data, sz); munmap(data, msg_size); for (i = 0; i < MAX_OPEN_FILES; i++) fd_vector[i].data = NULL; vars.usb = usb; vars.hide = 0; tcgetattr(STDIN_FILENO, &vars.to); tn = vars.to; tn.c_lflag &= ~(ICANON | ECHO); printf(FORMAT, TARGET_FORMAT); tcsetattr(STDIN_FILENO, TCSANOW, &tn); if (pthread_create(&thread, NULL, listenerTask, &vars)) host_print("listenerTask failed\n"); for (;;) { if (usb_read(usb, &i, 4) != 4) break; if (i == USBBOOT_FS_MAGIC) { vars.hide = 1; process_file(usb, rootfs, fd_vector); vars.hide = 0; continue; } printf("%c", i); fflush(stdout); } tcsetattr(STDIN_FILENO, TCSANOW, &vars.to); return 0; } int match_omap4_bootloader(struct usb_ifc_info *ifc) { if (ifc->dev_vendor != 0x0451) return -1; if ((ifc->dev_product != 0xD010) && (ifc->dev_product != 0xD00F)) return -1; return 0; } int main(int argc, char **argv) { void *data; unsigned sz; struct stat s; int fd; struct usb_handle *usb; int once; if (argc != 3) { printf("usage: %s \n", argv[0]); return 0; } argv++; fd = open(argv[0], O_RDONLY); if (fd < 0 || fstat(fd, &s)) { printf("cannot open '%s'\n", argv[0]); return -1; } data = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == MAP_FAILED) { printf("cannot mmap '%s'\n", argv[0]); return -1; } sz = s.st_size; close(fd); argv++; printf(FORMAT, HOST_FORMAT); for (once = 1;;) { usb = usb_open(match_omap4_bootloader); if (usb) return usb_boot(usb, data, sz, argv[0]); if (once) { once = 0; printf("waiting for OMAP44xx device...\n"); } usleep(250000); } return -1; }