diff options
Diffstat (limited to 'usbloader')
-rw-r--r-- | usbloader/Android.mk | 21 | ||||
-rw-r--r-- | usbloader/init.S | 96 | ||||
-rw-r--r-- | usbloader/main.c | 304 | ||||
-rw-r--r-- | usbloader/usbloader.c | 535 |
4 files changed, 956 insertions, 0 deletions
diff --git a/usbloader/Android.mk b/usbloader/Android.mk new file mode 100644 index 0000000..66c9e53 --- /dev/null +++ b/usbloader/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_ARM_MODE := arm + +LOCAL_C_INCLUDES := $(call include-path-for, bootloader mkbootimg) + +LOCAL_SRC_FILES := init.S main.c usbloader.c + +LOCAL_CFLAGS := -O2 -g -W -Wall +LOCAL_CFLAGS += -march=armv6 +LOCAL_CFLAGS += -DPRODUCTNAME='"$(strip $(TARGET_BOOTLOADER_BOARD_NAME))"' + +LOCAL_MODULE := usbloader + +LOCAL_MODULE_PATH := $(PRODUCT_OUT) +LOCAL_STATIC_LIBRARIES := $(TARGET_BOOTLOADER_LIBS) libboot libboot_c + +include $(BUILD_RAW_EXECUTABLE) + +$(LOCAL_BUILT_MODULE) : PRIVATE_LINK_SCRIPT := $(TARGET_BOOTLOADER_LINK_SCRIPT) diff --git a/usbloader/init.S b/usbloader/init.S new file mode 100644 index 0000000..9d048ea --- /dev/null +++ b/usbloader/init.S @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +.global irq_glue +.global irq_vector_table + +#include <boot/arm.h> + +v_reset: + b start +v_undefined: + b . +v_swi: + b . +v_prefetch_abt: + b . +v_data_abt: + b . +v_reserved: + b . +v_irq: + b . +v_fiq: + b . + +start: + ldr r5, =0xfffff000 + ands r4, pc, r5 + beq already_at_zero + + /* we're not loaded at 0 -- relocate us back down to where we belong */ + mov r5, #0 + ldr r6, =BOOTLOADER_END +1: ldr r7, [r4], #4 + str r7, [r5], #4 + cmp r5, r6 + bne 1b + + mov pc, #0 + +already_at_zero: + /* save registers for main() */ + mov r7, r0 + mov r8, r1 + mov r9, r2 + mov r10, r3 + + /* init stack */ + ldr r0, =BOOTLOADER_STACK + msr cpsr_c, #(PSR_I | PSR_F | PSR_SVC) + mov sp, r0 + + /* zero the BSS */ + ldr r1, =BOOTLOADER_BSS + ldr r2, =BOOTLOADER_END + mov r0, #0 +1: str r0, [r1], #4 + cmp r1, r2 + ble 1b + + bl periph_2gb_open + + /* restore registers for main() */ + mov r0, r7 + mov r1, r8 + mov r2, r9 + mov r3, r10 + + ldr r4, =_main + blx r4 + b . diff --git a/usbloader/main.c b/usbloader/main.c new file mode 100644 index 0000000..490b138 --- /dev/null +++ b/usbloader/main.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <boot/boot.h> +#include <boot/uart.h> +#include <boot/tags.h> +#include <boot/flash.h> +#include <boot/board.h> + +#include <bootimg.h> + +#define FLASH_PAGE_SIZE 2048 +#define FLASH_PAGE_BITS 11 + +#define ADDR_TAGS 0x10000100 + +static void create_atags(unsigned taddr, const char *cmdline, + unsigned raddr, unsigned rsize) +{ + unsigned n = 0; + unsigned pcount; + unsigned *tags = (unsigned *) taddr; + + // ATAG_CORE + tags[n++] = 2; + tags[n++] = 0x54410001; + + if(rsize) { + // ATAG_INITRD2 + tags[n++] = 4; + tags[n++] = 0x54420005; + tags[n++] = raddr; + tags[n++] = rsize; + } + + if((pcount = flash_get_ptn_count())){ + ptentry *ptn; + unsigned pn; + unsigned m = n + 2; + + for(pn = 0; pn < pcount; pn++) { + ptn = flash_get_ptn(pn); + memcpy(tags + m, ptn, sizeof(ptentry)); + m += (sizeof(ptentry) / sizeof(unsigned)); + } + + tags[n + 0] = m - n; + tags[n + 1] = 0x4d534d70; + n = m; + } + if(cmdline && cmdline[0]) { + const char *src; + char *dst; + unsigned len = 0; + + dst = (char*) (tags + n + 2); + src = cmdline; + while((*dst++ = *src++)) len++; + + len++; + len = (len + 3) & (~3); + + // ATAG_CMDLINE + tags[n++] = 2 + (len / 4); + tags[n++] = 0x54410009; + + n += (len / 4); + } + + // ATAG_NONE + tags[n++] = 0; + tags[n++] = 0; +} + +static void boot_linux(unsigned kaddr) +{ + void (*entry)(unsigned,unsigned,unsigned) = (void*) kaddr; + + entry(0, board_machtype(), ADDR_TAGS); +} + +unsigned char raw_header[2048]; + +int boot_linux_from_flash(void) +{ + boot_img_hdr *hdr = (void*) raw_header; + unsigned n; + ptentry *p; + unsigned offset = 0; + const char *cmdline; + + if((p = flash_find_ptn("boot")) == 0) { + cprintf("NO BOOT PARTITION\n"); + return -1; + } + + if(flash_read(p, offset, raw_header, 2048)) { + cprintf("CANNOT READ BOOT IMAGE HEADER\n"); + return -1; + } + offset += 2048; + + if(memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + cprintf("INVALID BOOT IMAGE HEADER\n"); + return -1; + } + + n = (hdr->kernel_size + (FLASH_PAGE_SIZE - 1)) & (~(FLASH_PAGE_SIZE - 1)); + if(flash_read(p, offset, (void*) hdr->kernel_addr, n)) { + cprintf("CANNOT READ KERNEL IMAGE\n"); + return -1; + } + offset += n; + + n = (hdr->ramdisk_size + (FLASH_PAGE_SIZE - 1)) & (~(FLASH_PAGE_SIZE - 1)); + if(flash_read(p, offset, (void*) hdr->ramdisk_addr, n)) { + cprintf("CANNOT READ RAMDISK IMAGE\n"); + return -1; + } + offset += n; + + dprintf("\nkernel @ %x (%d bytes)\n", hdr->kernel_addr, hdr->kernel_size); + dprintf("ramdisk @ %x (%d bytes)\n\n\n", hdr->ramdisk_addr, hdr->ramdisk_size); + + if(hdr->cmdline[0]) { + cmdline = (char*) hdr->cmdline; + } else { + cmdline = board_cmdline(); + if(cmdline == 0) { + cmdline = "mem=50M console=null"; + } + } + cprintf("cmdline = '%s'\n", cmdline); + + cprintf("\nBooting Linux\n"); + + create_atags(ADDR_TAGS, cmdline, + hdr->ramdisk_addr, hdr->ramdisk_size); + + boot_linux(hdr->kernel_addr); + return 0; +} + +void usbloader_init(void); +void uart_putc(unsigned); +const char *get_fastboot_version(void); + +extern unsigned linux_type; +extern unsigned linux_tags; + +static unsigned revision = 0; + +char serialno[32]; + +void dump_smem_info(void); + +static void tag_dump(unsigned tag, void *data, unsigned sz, void *cookie) +{ + dprintf("tag type=%x data=%x size=%x\n", tag, (unsigned) data, sz); +} + +static struct tag_handler tag_dump_handler = { + .func = tag_dump, + .type = 0, +}; + +void xdcc_putc(unsigned x) +{ + while (dcc_putc(x) < 0) ; +} + +#define SERIALNO_STR "androidboot.serialno=" +#define SERIALNO_LEN strlen(SERIALNO_STR) + +static int boot_from_flash = 1; + +void key_changed(unsigned int key, unsigned int down) +{ + if(!down) return; + if(key == BOOT_KEY_STOP_BOOT) boot_from_flash = 0; +} + +static int tags_okay(unsigned taddr) +{ + unsigned *tags = (unsigned*) taddr; + + if(taddr != ADDR_TAGS) return 0; + if(tags[0] != 2) return 0; + if(tags[1] != 0x54410001) return 0; + + return 1; +} + +int _main(unsigned zero, unsigned type, unsigned tags) +{ + const char *cmdline = 0; + int n; + + arm11_clock_init(); + + /* must do this before board_init() so that we + ** use the partition table in the tags if it + ** already exists + */ + if((zero == 0) && (type != 0) && tags_okay(tags)) { + linux_type = type; + linux_tags = tags; + + cmdline = tags_get_cmdline((void*) linux_tags); + + tags_import_partitions((void*) linux_tags); + revision = tags_get_revision((void*) linux_tags); + if(revision == 1) { + console_set_colors(0x03E0, 0xFFFF); + } + if(revision == 2) { + console_set_colors(0x49B2, 0xFFFF); + } + + /* we're running as a second-stage, so wait for interrupt */ + boot_from_flash = 0; + } else { + linux_type = board_machtype(); + linux_tags = 0; + } + + board_init(); + keypad_init(); + + console_init(); + dprintf_set_putc(uart_putc); + + if(linux_tags == 0) { + /* generate atags containing partitions + * from the bootloader, etc + */ + linux_tags = ADDR_TAGS; + create_atags(linux_tags, 0, 0, 0); + } + + if (cmdline) { + char *sn = strstr(cmdline, SERIALNO_STR); + if (sn) { + char *s = serialno; + sn += SERIALNO_LEN; + while (*sn && (*sn != ' ') && ((s - serialno) < 31)) { + *s++ = *sn++; + } + *s++ = 0; + } + } + + cprintf("\n\nUSB FastBoot: V%s\n", get_fastboot_version()); + cprintf("Machine ID: %d v%d\n", linux_type, revision); + cprintf("Build Date: "__DATE__", "__TIME__"\n\n"); + + cprintf("Serial Number: %s\n\n", serialno[0] ? serialno : "UNKNOWN"); + + flash_dump_ptn(); + + flash_init(); + + /* scan the keyboard a bit */ + for(n = 0; n < 50; n++) { + boot_poll(); + } + + if (boot_from_flash) { + cprintf("\n ** BOOTING LINUX FROM FLASH **\n"); + boot_linux_from_flash(); + } + + usbloader_init(); + + for(;;) { + usb_poll(); + } + return 0; +} diff --git a/usbloader/usbloader.c b/usbloader/usbloader.c new file mode 100644 index 0000000..ca7b4fa --- /dev/null +++ b/usbloader/usbloader.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <boot/boot.h> +#include <boot/flash.h> +#include <boot/board.h> +#include <boot/usb.h> + +#include <boot/bootimg.h> +#include <boot/tags.h> + +#include <boot/gpio.h> + +#define VERSION "0.5" + +#define REQUIRE_SIGNATURE 0 + +#if REQUIRE_SIGNATURE +unsigned key_engineering[2 + 64 + 64] = { + 64,0x5b022317,-60769447,648742897,-13657530,585562035,591851935, + 454860199,-1809625305,1868200692,-155297008,-1688439840,-1333607631, + -483027189,-2051438457,1030069735,819944365,2133377257,-1978924214, + 2109678622,1974978919,-1811463608,765849268,1984092281,921245328, + -1055062768,1487475997,1209618652,871985152,-611178965,-2057018571, + 335641539,-1196119550,1550548229,-356223887,1909799623,1281016007, + 957001635,1005656532,-1027634024,-1576447610,-1917246637,589192795, + -1137386186,-1958135372,1933245070,64958951,-1820428322,-1577697840, + 1824253519,555306239,-1588272058,-1925773018,1205934271,-836584444, + -1140961670,-185198349,1293769947,37045923,1516796974,-297288651, + 651582073,-1337054592,-543971216,-1706823885,-1040652818,-594113104, + 260093481,-1277656496,56493468,1577037283,773995876,244894933, + -2075797967,783894843,880611008,-1433369702,380946504,-2081431477, + 1377832804,2089455451,-410001201,1245307237,-1228170341,-2062569137, + -1327614308,-1671042654,1242248660,-418803721,40890010,-1806767460, + -1468529145,-1058158532,1243817302,-527795003,175453645,-210650325, + -827053868,-571422860,886300657,2129677324,846504590,-1413102805, + -1287448511,-1991140134,56194155,1375685594,-129884114,1393568535, + -1098719620,-935279550,1717137954,-1782544741,272581921,-669183778, + 584824755,1434974827,-1122387971,-810584927,-2147338547,-937541680, + -313561073,5506366,-1594059648,-1744451574,1896015834,1496367069, + 1742853908,508461291,1905056764 +}; +#endif + +const char *get_fastboot_version(void) +{ + return VERSION; +} + +unsigned linux_type = 0; +unsigned linux_tags = 0; + +unsigned ramdisk_addr = 0x10400000; +unsigned ramdisk_size = 0; +unsigned kernel_addr = 0x10008000; +unsigned kernel_size = 0; + +static void fixup_tags(unsigned *tags, unsigned *out, const char *cmdline) +{ + unsigned *newtags = (unsigned *) 0x10004000; + unsigned *np = newtags; + unsigned n; + char *oldcmdline = ""; + + if(cmdline == 0) cmdline = ""; + + /* CORE */ + *np++ = 2; + *np++ = 0x54410001; + + if(tags != 0) { + while(*tags) { + if(tags[1] == 0x54410001) { + /* skip core tag */ + tags += tags[0]; + } else if((tags[1] == 0x54420005) && (ramdisk_size != 0)) { + /* skip ramdisk if we have one of our own */ + tags += tags[0]; + } else if((tags[1] == 0x54410009) && (cmdline[0])) { + /* skip existing cmdline so we can replace it */ + oldcmdline = (char*) (tags + 2); + tags += tags[0]; + } else { + /* copy any unknown tags */ + n = tags[0]; + while(n-- > 0) { + *np++ = *tags++; + } + } + } + } + + /* create a ramdisk tag if we need to */ + if(ramdisk_size) { + *np++ = 4; + *np++ = 0x54420005; + *np++ = ramdisk_addr; + *np++ = ramdisk_size; + } + + dprintf("cmdline: '%s'\n", oldcmdline); + dprintf("cmdline: '%s'\n", cmdline); + + /* create a cmdline tag if we need to */ + if(cmdline[0]) { + int len; + char *str = (char*) (np + 2); + + len = strlen(oldcmdline); + if(len) { + memcpy(str, oldcmdline, len); + str += len; + *str++ = ' '; + } + + len = strlen(cmdline); + memcpy(str, cmdline, len); + str += len; + *str++ = 0; + + /* length in words */ + len = ((str - ((char*) (np + 2))) + 3) / 4; + + dprintf("CMDLINE: '%s'\n", ((char*) (np + 2))); + + *np++ = 2 + len; + *np++ = 0x54410009; + + np += len; + } + + /* add footer tag */ + *np++ = 0; + *np++ = 0; + + /* copy it all back to the original tags area */ + while(newtags < np) { + *out++ = *newtags++; + } +} + +static char cmdline[BOOT_ARGS_SIZE]; + +static void boot_linux(void) +{ + unsigned *tags = (unsigned*) 0x10000100; + void (*entry)(unsigned,unsigned,unsigned) = (void*) kernel_addr; + + if(linux_type == 0) { + linux_type = board_machtype(); + } + + fixup_tags((unsigned*) linux_tags, tags, cmdline); + + entry(0, linux_type, tags); + + for(;;); +} + +/* convert a boot_image at kernel_addr into a kernel + ramdisk + tags */ +static int init_boot_linux(void) +{ + boot_img_hdr *hdr = (void*) kernel_addr; + unsigned page_mask = 2047; + unsigned kernel_actual; + unsigned ramdisk_actual; + unsigned second_actual; + + if((kernel_size < 2048) || memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)){ + dprintf("bootimg: bad header\n"); + return -1; + } + + if(hdr->page_size != 2048) { + dprintf("bootimg: invalid page size\n"); + return -1; + } + + kernel_actual = (hdr->kernel_size + page_mask) & (~page_mask); + ramdisk_actual = (hdr->ramdisk_size + page_mask) & (~page_mask); + second_actual = (hdr->second_size + page_mask) & (~page_mask); + + if(kernel_size != (kernel_actual + ramdisk_actual + second_actual + 2048)) { + dprintf("bootimg: invalid image size"); + return -1; + } + + /* XXX process commandline here */ + if(hdr->cmdline[0]){ + hdr->cmdline[BOOT_ARGS_SIZE - 1] = 0; + memcpy(cmdline, hdr->cmdline, BOOT_ARGS_SIZE); + } + + /* XXX how to validate addresses? */ + ramdisk_addr = hdr->ramdisk_addr; + ramdisk_size = hdr->ramdisk_size; + + kernel_addr = hdr->kernel_addr; + kernel_size = hdr->kernel_size; + + dprintf("bootimg: kernel addr=%x size=%x\n", + kernel_addr, kernel_size); + dprintf("bootimg: ramdisk addr=%x size=%x\n", + ramdisk_addr, ramdisk_size); + + memcpy((void*) ramdisk_addr, + hdr->magic + 2048 + kernel_actual, + ramdisk_size); + + memcpy((void*) kernel_addr, + hdr->magic + 2048, + kernel_size); + + return 0; +} + +static struct usb_endpoint *ep1in, *ep1out; +static struct usb_request *rx_req, *tx_req; +static unsigned rx_addr; +static unsigned rx_length; + +static char *cmdbuf; + +static void usb_rx_cmd_complete(struct usb_request *req, unsigned actual, int status); +static void usb_rx_data_complete(struct usb_request *req, unsigned actual, int status); + +static void rx_cmd(void) +{ + struct usb_request *req = rx_req; + req->buf = cmdbuf; + req->length = 4096; + req->complete = usb_rx_cmd_complete; + usb_queue_req(ep1out, req); +} + +static void rx_data(void) +{ + struct usb_request *req = rx_req; + req->buf = (void*) rx_addr; + req->length = (rx_length > 4096) ? 4096 : rx_length; + req->complete = usb_rx_data_complete; + usb_queue_req(ep1out, req); +} + +static void tx_status(const char *status) +{ + struct usb_request *req = tx_req; + int len = strlen(status); +// dprintf("tx_status('%s')\n", status); + memcpy(req->buf, status, len); + req->length = len; + req->complete = 0; + usb_queue_req(ep1in, req); +} + +static void usb_rx_data_complete(struct usb_request *req, unsigned actual, int status) +{ + if(status != 0) return; + + if(actual > rx_length) { + actual = rx_length; + } + + rx_addr += actual; + rx_length -= actual; + + if(rx_length > 0) { + rx_data(); + } else { + tx_status("OKAY"); + rx_cmd(); + } +} + +static unsigned hex2unsigned(char *x) +{ + unsigned n = 0; + + while(*x) { + switch(*x) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = (n << 4) | (*x - '0'); + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + n = (n << 4) | (*x - 'a' + 10); + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + n = (n << 4) | (*x - 'A' + 10); + break; + default: + return n; + } + x++; + } + + return n; +} + +static void num_to_hex8(unsigned n, char *out) +{ + static char tohex[16] = "0123456789abcdef"; + int i; + for(i = 7; i >= 0; i--) { + out[i] = tohex[n & 15]; + n >>= 4; + } + out[8] = 0; +} + +extern char serialno[]; + +static char signature[SIGNATURE_SIZE]; + +static void usb_rx_cmd_complete(struct usb_request *req, unsigned actual, int status) +{ + if(status != 0) return; + + if(actual > 4095) actual = 4095; + cmdbuf[actual] = 0; + + dprintf("\n> %s\n",cmdbuf); + +// dprintf("usb_rx_cmd_complete() '%s'\n", cmdbuf); + + if(memcmp(cmdbuf, "reboot", 6) == 0) { + tx_status("OKAY"); + rx_cmd(); + mdelay(100); + board_reboot(); + } +#if 0 + if(memcmp(cmdbuf, "debug:", 6) == 0) { + void debug(char *cmd, char *resp); + memcpy(cmdbuf, "OKAY", 5); + tx_status(cmdbuf); + rx_cmd(); + mdelay(5000); + dprintf("NOW!\n"); + debug(cmdbuf + 6, cmdbuf + 4); + return; + } +#endif + if(memcmp(cmdbuf, "getvar:", 7) == 0) { + char response[64]; + strcpy(response,"OKAY"); + + if(!strcmp(cmdbuf + 7, "version")) { + strcpy(response + 4, VERSION); + } else if(!strcmp(cmdbuf + 7, "product")) { + strcpy(response + 4, PRODUCTNAME); + } else if(!strcmp(cmdbuf + 7, "serialno")) { + strcpy(response + 4, serialno); + } else { + board_getvar(cmdbuf + 7, response + 4); + } + tx_status(response); + rx_cmd(); + return; + } + + if(memcmp(cmdbuf, "download:", 9) == 0) { + char status[16]; + rx_addr = kernel_addr; + rx_length = hex2unsigned(cmdbuf + 9); + if (rx_length > (64*1024*1024)) { + tx_status("FAILdata too large"); + rx_cmd(); + return; + } + kernel_size = rx_length; + dprintf("recv data addr=%x size=%x\n", rx_addr, rx_length); + strcpy(status,"DATA"); + num_to_hex8(rx_length, status + 4); + tx_status(status); + rx_data(); + return; + } + + if(memcmp(cmdbuf, "erase:", 6) == 0){ + struct ptentry *ptn; + ptn = flash_find_ptn(cmdbuf + 6); + if(ptn == 0) { + tx_status("FAILpartition does not exist"); + rx_cmd(); + return; + } + dprintf("erasing '%s'\n", ptn->name); + cprintf("erasing '%s'", ptn->name); + if(flash_erase(ptn)) { + tx_status("FAILfailed to erase partition"); + rx_cmd(); + cprintf(" - FAIL\n"); + return; + } else { + dprintf("partition '%s' erased\n", ptn->name); + cprintf(" - OKAY\n"); + } + tx_status("OKAY"); + rx_cmd(); + return; + } + + if(memcmp(cmdbuf, "flash:", 6) == 0){ + struct ptentry *ptn; + int extra = 0; + ptn = flash_find_ptn(cmdbuf + 6); + if(kernel_size == 0) { + tx_status("FAILno image downloaded"); + rx_cmd(); + return; + } + if(ptn == 0) { + tx_status("FAILpartition does not exist"); + rx_cmd(); + return; + } + if(!strcmp(ptn->name,"boot") || !strcmp(ptn->name,"recovery")) { + if(memcmp((void*) kernel_addr, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + tx_status("FAILimage is not a boot image"); + rx_cmd(); + return; + } + } +#if REQUIRE_SIGNATURE + { + unsigned char digest[DIGEST_SIZE]; + compute_digest((void*) kernel_addr, kernel_size, digest); + if (is_signature_okay(digest, signature, key_engineering)) { + dprintf("verified by engineering key\n"); + } else { + tx_status("FAILsignature did not verify"); + rx_cmd(); + return; + } + } +#endif + if(!strcmp(ptn->name,"system") || !strcmp(ptn->name,"userdata")) { + extra = 64; + } else { + kernel_size = (kernel_size + 2047) & (~2047); + } + dprintf("writing %d bytes to '%s'\n", + kernel_size, ptn->name); + cprintf("writing '%s' (%d bytes)", ptn->name, kernel_size); + if(flash_write(ptn, extra, (void*) kernel_addr, kernel_size)) { + tx_status("FAILflash write failure"); + rx_cmd(); + cprintf(" - FAIL\n"); + return; + } else { + dprintf("partition '%s' updated\n", ptn->name); + cprintf(" - OKAY\n"); + } + tx_status("OKAY"); + rx_cmd(); + return; + } + if(memcmp(cmdbuf, "boot", 4) == 0) { + if(init_boot_linux()) { + tx_status("FAILinvalid boot image"); + rx_cmd(); + return; + } + dprintf("booting linux...\n"); + cprintf("\nbooting linux...\n"); + tx_status("OKAY"); + mdelay(10); + usb_shutdown(); + boot_linux(); + return; + } + if(memcmp(cmdbuf, "signature", 9) == 0) { + if (kernel_size != SIGNATURE_SIZE) { + tx_status("FAILsignature not 256 bytes long"); + rx_cmd(); + return; + } + memcpy(signature, (void*)kernel_addr, SIGNATURE_SIZE); + tx_status("OKAY"); + rx_cmd(); + return; + } + + tx_status("FAILinvalid command"); + rx_cmd(); +} + +void usb_status(unsigned online, unsigned highspeed) +{ + if(online) { + dprintf("usb: online (%s)\n", highspeed ? "highspeed" : "fullspeed"); + rx_cmd(); + } +} + +void usbloader_init(void) +{ + usb_init(); + + ep1out = usb_endpoint_alloc(1, 0, 512); + ep1in = usb_endpoint_alloc(1, 1, 512); + rx_req = usb_request_alloc(4096); + tx_req = usb_request_alloc(4096); + cmdbuf = rx_req->buf; + + boot_register_poll_func(usb_poll); +} |