diff options
| author | mattis fjallstrom <mattis@acm.org> | 2015-09-06 11:09:39 -0700 |
|---|---|---|
| committer | mattis fjallstrom <mattis@acm.org> | 2015-09-06 11:09:39 -0700 |
| commit | ee87ec4d2c955c929a4c27ce8a56f918444cf955 (patch) | |
| tree | c18141755ad893eddd5b504e4cf096ad4e7062b9 /common | |
| parent | 2c25de1ed5c6f8f6bba3b5ec506f430d8a883a83 (diff) | |
| download | olio-uboot-2014.01-ee87ec4d2c955c929a4c27ce8a56f918444cf955.tar.xz olio-uboot-2014.01-ee87ec4d2c955c929a4c27ce8a56f918444cf955.zip | |
First fastboot commit, MLO built here wont work so be careful.
Change-Id: Ic8d65a92da82896282eee71cf0d0515f64c939bc
Diffstat (limited to 'common')
| -rw-r--r-- | common/Makefile | 2 | ||||
| -rw-r--r-- | common/cmd_fastboot.c | 117 | ||||
| -rw-r--r-- | common/cmd_fastboot_omap.c | 2544 | ||||
| -rw-r--r-- | common/cmd_nand.c | 2 | ||||
| -rw-r--r-- | common/cmd_nvedit.c | 4 |
5 files changed, 2666 insertions, 3 deletions
diff --git a/common/Makefile b/common/Makefile index d12cba5bf..de4253401 100644 --- a/common/Makefile +++ b/common/Makefile @@ -163,6 +163,8 @@ obj-y += cmd_usb.o obj-y += usb.o usb_hub.o obj-$(CONFIG_USB_STORAGE) += usb_storage.o endif +obj-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o + obj-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o obj-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o obj-$(CONFIG_CMD_XIMG) += cmd_ximg.o diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c new file mode 100644 index 000000000..4ccf9af86 --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de> + * + * Modified by Vishveshwar Bhat <vishveshwar.bhat@ti.com> + * Pankaj Bharadiya <pankaj.bharadiya@ti.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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <command.h> +#include <usb/fastboot.h> +#include <asm/sizes.h> + +static char serial_number[28] = "001234"; + +static struct usb_string def_usb_fb_strings[] = { + /* This has to match the product name used in the AOSP build. + Should not be hard coded here... */ + /* { FB_STR_PRODUCT_IDX, "AM335xevm" }, */ + { FB_STR_PRODUCT_IDX, "beagleboneblack" }, + { FB_STR_SERIAL_IDX, serial_number }, + { FB_STR_PROC_REV_IDX, "ES2.x" }, + { FB_STR_PROC_TYPE_IDX, "ARMv7" }, + { FB_STR_MANUFACTURER_IDX, "Texas Instruments" }, + { } +}; + +static struct usb_gadget_strings def_fb_strings = { + .language = 0x0409, /* en-us */ + .strings = def_usb_fb_strings, +}; + +/* + * Hardcoded memory region to stash data which comes over USB before it is + * stored on media + */ +DECLARE_GLOBAL_DATA_PTR; +#define CFG_FASTBOOT_TRANSFER_BUFFER (void *)(gd->bd->bi_dram[0].start + SZ_16M) + +static void set_serial_number(void) +{ + /* use ethaddr for fastboot serial no. */ + char *ethaddr = "123456789012345678901234567890";/*getenv("ethaddr");*/ + + if (ethaddr != NULL) { + int len; + + memset(&serial_number[0], 0, 28); + len = strlen(ethaddr); + if (len > 28) + len = 26; + + strncpy(&serial_number[0], ethaddr, len); + } +} + +int fastboot_board_init(struct fastboot_config *interface, + struct usb_gadget_strings **str) +{ + /* Initialize board serial no. */ + set_serial_number(); + + interface->transfer_buffer = CFG_FASTBOOT_TRANSFER_BUFFER; + interface->transfer_buffer_size = CONFIG_FASTBOOT_MAX_TRANSFER_SIZE; + interface->nand_block_size = FASTBOOT_NAND_BLOCK_SIZE; + interface->nand_oob_size = FASTBOOT_NAND_OOB_SIZE; + interface->download_bytes = 0; + interface->download_size = 0; + + *str = &def_fb_strings; + return 0; +} + +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = 1; + + printf("Fastboot dbg...\n"); + if (!fastboot_init()) { + //board_mmc_fbtptn_init(); + printf("Fastboot entered...\n"); + + ret = 0; + + while (1) { + if (fastboot_poll()) + { + printf("Fastboot poll break...\n"); + break; + } + } + } + + fastboot_shutdown(); + return ret; +} + +U_BOOT_CMD( + fastboot, 1, 1, do_fastboot, + "fastboot- use USB Fastboot protocol\n", + "" +); diff --git a/common/cmd_fastboot_omap.c b/common/cmd_fastboot_omap.c new file mode 100644 index 000000000..c0b5c283e --- /dev/null +++ b/common/cmd_fastboot_omap.c @@ -0,0 +1,2544 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * Copyright (C) 2010 Texas Instruments + * + * Author : Mohammed Afzal M A <afzal@ti.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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * Fastboot is implemented using gadget stack, many of the ideas are + * derived from fastboot implemented in OmapZoom by + * Tom Rix <Tom.Rix@windriver.com>, and portion of the code has been + * ported from OmapZoom. + * + * Part of OmapZoom was copied from Android project, Android source + * (legacy bootloader) was used indirectly here by using OmapZoom. + * + * This is Android's Copyright: + * + * 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 <common.h> +#include <command.h> +#include <malloc.h> +#include <usb/fastboot.h> +#include <omap_fastboot.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* If a BUILD_TAG was passed as an argument to make, use it + * for the fastboot version. Otherwise, see if a board file + * defined a CONFIG_FASTBOOT_VERSION_BOOTLOADER and if so, use + * that. Otherwise, use an automatically constructed one of the form: + * productnameYMDHB + * where: + * productname is FASTBOOT_PRODUCT_NAME + * Y is the year with 'A' being 2011 and incrementing from there + * M is the month + * D is the day of the month + * H is the hour + * B is the minute divided by two + * All of the fields are based upon the build time. M, D, H and B are all + * output in base 36 (i.e. each digit is in the set 0-9A-Z). A quick and + * dirty Python program to convert one of these versions back to the build + * time: + +#! /usr/bin/env python +import sys +foo = sys.argv[1][-5:] +print "%04d/%02d/%02d %02d:%02d\n" % (int(foo[0], 36) + 2001, + int(foo[1], 36), + int(foo[2], 36), + int(foo[3], 36), + int(foo[4], 36) * 2) + */ +#ifdef BUILD_TAG +#undef CONFIG_FASTBOOT_VERSION_BOOTLOADER +#define CONFIG_FASTBOOT_VERSION_BOOTLOADER BUILD_TAG +#else +#ifndef CONFIG_FASTBOOT_VERSION_BOOTLOADER +#include <timestamp_autogenerated.h> +#define CONFIG_FASTBOOT_VERSION_BOOTLOADER \ + (FASTBOOT_PRODUCT_NAME FASTBOOT_TIMESTAMP) +#endif +#endif + +#define FASTBOOT_RUN_RECOVERY_ENV_NAME "fastboot_run_recovery" +#define FASTBOOT_UNLOCKED_ENV_NAME "fastboot_unlocked" +#define FASTBOOT_UNLOCK_TIMEOUT_SECS 5 + +#ifndef CONFIG_ENV_BLK_PARTITION +#define CONFIG_ENV_BLK_PARTITION "environment" +#endif +#ifndef CONFIG_INFO_PARTITION +#define CONFIG_INFO_PARTITION "device_info" +#endif + +#define ERR +#define WARN +#undef INFO +#undef DEBUG + +#ifndef CONFIG_FASTBOOT_LOG_SIZE +#define CONFIG_FASTBOOT_LOG_SIZE 4000 +#endif +static char log_buffer[CONFIG_FASTBOOT_LOG_SIZE]; +static uint32_t log_position; + + +#ifdef DEBUG +#define FBTDBG(fmt, args...)\ + printf("DEBUG: [%s]: %d:\n"fmt, __func__, __LINE__, ##args) +#else +#define FBTDBG(fmt, args...) do {} while (0) +#endif + +#ifdef INFO +#define FBTINFO(fmt, args...)\ + printf("INFO: [%s]: "fmt, __func__, ##args) +#else +#define FBTINFO(fmt, args...) do {} while (0) +#endif + +#ifdef WARN +#define FBTWARN(fmt, args...)\ + printf("WARNING: [%s]: "fmt, __func__, ##args) +#else +#define FBTWARN(fmt, args...) do {} while (0) +#endif + +#ifdef ERR +#define FBTERR(fmt, args...)\ + printf("ERROR: [%s]: "fmt, __func__, ##args) +#else +#define FBTERR(fmt, args...) do {} while (0) +#endif + +#include <exports.h> +#include <environment.h> + +/* USB specific */ + +#include <usb_defs.h> + +#if defined(CONFIG_PPC) +#include <usb/mpc8xx_udc.h> +#elif defined(CONFIG_OMAP1510) +#include <usb/omap1510_udc.h> +#elif defined(CONFIG_MUSB_UDC) +#include <usb/musb_udc.h> +#elif defined(CONFIG_PXA27X) +#include <usb/pxa27x_udc.h> +#elif defined(CONFIG_SPEAR3XX) || defined(CONFIG_SPEAR600) +#include <usb/spr_udc.h> +#endif + +#if defined (CONFIG_OMAP) +#include <asm/arch/sys_proto.h> +#endif + +#define STR_LANG 0x00 +#define STR_MANUFACTURER 0x01 +#define STR_PRODUCT 0x02 +#define STR_SERIAL 0x03 +#define STR_CONFIGURATION 0x04 +#define STR_INTERFACE 0x05 +#define STR_COUNT 0x06 + +#define CONFIG_USBD_CONFIGURATION_STR "Android Fastboot Configuration" +#define CONFIG_USBD_INTERFACE_STR "Android Fastboot Interface" + +#define USBFBT_BCD_DEVICE 0x00 +#define USBFBT_MAXPOWER 0x32 + +#define USB_FLUSH_DELAY_MICROSECS 1000 + +#define NUM_CONFIGS 1 +#define NUM_INTERFACES 1 +#define NUM_ENDPOINTS 2 + +#define RX_EP_INDEX 1 +#define TX_EP_INDEX 2 + +struct _fbt_config_desc { + struct usb_configuration_descriptor configuration_desc; + struct usb_interface_descriptor interface_desc; + struct usb_endpoint_descriptor endpoint_desc[NUM_ENDPOINTS]; +}; + +static void fbt_handle_response(void); +static disk_partition_t *fastboot_flash_find_ptn(const char *name); +static void fbt_run_recovery_wipe_data(void); +static void fbt_run_recovery(int do_saveenv); + +/* defined and used by gadget/ep0.c */ +extern struct usb_string_descriptor **usb_strings; + +/* USB Descriptor Strings */ +static char serial_number[33]; /* what should be the length ?, 33 ? */ +static u8 wstr_lang[4] = {4, USB_DT_STRING, 0x9, 0x4}; +static u8 wstr_manufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)]; +static u8 wstr_product[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)]; +static u8 wstr_serial[2 + 2*(sizeof(serial_number) - 1)]; +static u8 wstr_configuration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)]; +static u8 wstr_interface[2 + 2*(sizeof(CONFIG_USBD_INTERFACE_STR)-1)]; + +/* USB descriptors */ +static struct usb_device_descriptor device_descriptor = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(USB_BCD_VERSION), + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE, + .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID), + .idProduct = cpu_to_le16(CONFIG_USBD_PRODUCTID), + .bcdDevice = cpu_to_le16(USBFBT_BCD_DEVICE), + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIAL, + .bNumConfigurations = NUM_CONFIGS +}; + +static struct _fbt_config_desc fbt_config_desc = { + .configuration_desc = { + .bLength = sizeof(struct usb_configuration_descriptor), + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(sizeof(struct _fbt_config_desc)), + .bNumInterfaces = NUM_INTERFACES, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIGURATION, + .bmAttributes = BMATTRIBUTE_SELF_POWERED | BMATTRIBUTE_RESERVED, + .bMaxPower = USBFBT_MAXPOWER, + }, + .interface_desc = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0x2, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, + .iInterface = STR_INTERFACE, + }, + .endpoint_desc = { + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + /* XXX: can't the address start from 0x1, currently + seeing problem with "epinfo" */ + .bEndpointAddress = RX_EP_INDEX | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bInterval = 0xFF, + }, + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + /* XXX: can't the address start from 0x1, currently + seeing problem with "epinfo" */ + .bEndpointAddress = TX_EP_INDEX | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bInterval = 0xFF, + }, + }, +}; + +static struct usb_interface_descriptor interface_descriptors[NUM_INTERFACES]; +static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS]; + +static struct usb_string_descriptor *fbt_string_table[STR_COUNT]; +static struct usb_device_instance device_instance[1]; +static struct usb_bus_instance bus_instance[1]; +static struct usb_configuration_instance config_instance[NUM_CONFIGS]; +static struct usb_interface_instance interface_instance[NUM_INTERFACES]; +static struct usb_alternate_instance alternate_instance[NUM_INTERFACES]; +static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS + 1]; + +/* FASBOOT specific */ + +/* U-boot version */ +extern char version_string[]; + +static const char info_partition_magic[] = {'I', 'n', 'f', 'o'}; + +static struct cmd_fastboot_interface priv = { + .transfer_buffer = (u8 *)CONFIG_FASTBOOT_TRANSFER_BUFFER, + .transfer_buffer_size = CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE, +}; + +static void fbt_init_endpoints(void); +static int do_booti(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); + +extern int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); +/* Use do_bootm_linux and do_go for fastboot's 'boot' command */ +extern int do_go(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); +extern int do_bootm_linux(int flag, int argc, char *argv[], + bootm_headers_t *images); +extern int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]); + +/* To support the Android-style naming of flash */ +#define MAX_PTN (CONFIG_MAX_PARTITION_NUM - CONFIG_MIN_PARTITION_NUM + 1) +static disk_partition_t ptable[MAX_PTN]; +static unsigned int pcount; + +/* USB specific */ + +/* utility function for converting char * to wide string used by USB */ +static void str2wide(char *str, u16 * wide) +{ + int i; + for (i = 0; i < strlen(str) && str[i]; i++) { + #if defined(__LITTLE_ENDIAN) + wide[i] = (u16) str[i]; + #elif defined(__BIG_ENDIAN) + wide[i] = ((u16)(str[i])<<8); + #else + #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined" + #endif + } +} + +/* fastboot_init has to be called before this fn to get correct serial string */ +static void fbt_init_strings(void) +{ + struct usb_string_descriptor *string; + + fbt_string_table[STR_LANG] = (struct usb_string_descriptor *)wstr_lang; + + string = (struct usb_string_descriptor *) wstr_manufacturer; + string->bLength = sizeof(wstr_manufacturer); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_USBD_MANUFACTURER, string->wData); + fbt_string_table[STR_MANUFACTURER] = string; + + string = (struct usb_string_descriptor *) wstr_product; + string->bLength = sizeof(wstr_product); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_USBD_PRODUCT_NAME, string->wData); + fbt_string_table[STR_PRODUCT] = string; + + string = (struct usb_string_descriptor *) wstr_serial; + string->bLength = sizeof(wstr_serial); + string->bDescriptorType = USB_DT_STRING; + str2wide(serial_number, string->wData); + fbt_string_table[STR_SERIAL] = string; + + string = (struct usb_string_descriptor *) wstr_configuration; + string->bLength = sizeof(wstr_configuration); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_USBD_CONFIGURATION_STR, string->wData); + fbt_string_table[STR_CONFIGURATION] = string; + + string = (struct usb_string_descriptor *) wstr_interface; + string->bLength = sizeof(wstr_interface); + string->bDescriptorType = USB_DT_STRING; + str2wide(CONFIG_USBD_INTERFACE_STR, string->wData); + fbt_string_table[STR_INTERFACE] = string; + + /* Now, initialize the string table for ep0 handling */ + usb_strings = fbt_string_table; +} + +static void fbt_event_handler (struct usb_device_instance *device, + usb_device_event_t event, int data) +{ + switch (event) { + case DEVICE_RESET: + case DEVICE_BUS_INACTIVE: + priv.configured = 0; + break; + case DEVICE_CONFIGURED: + priv.configured = 1; + break; + + case DEVICE_ADDRESS_ASSIGNED: + fbt_init_endpoints(); + + default: + break; + } +} + +/* fastboot_init has to be called before this fn to get correct serial string */ +static void fbt_init_instances(void) +{ + int i; + + /* initialize device instance */ + memset(device_instance, 0, sizeof(struct usb_device_instance)); + device_instance->device_state = STATE_INIT; + device_instance->device_descriptor = &device_descriptor; + device_instance->event = fbt_event_handler; + device_instance->cdc_recv_setup = NULL; + device_instance->bus = bus_instance; + device_instance->configurations = NUM_CONFIGS; + device_instance->configuration_instance_array = config_instance; + + /* XXX: what is this bus instance for ?, can't it be removed by moving + endpoint_array and serial_number_str is moved to device instance */ + /* initialize bus instance */ + memset(bus_instance, 0, sizeof(struct usb_bus_instance)); + bus_instance->device = device_instance; + bus_instance->endpoint_array = endpoint_instance; + /* XXX: what is the relevance of max_endpoints & maxpacketsize ? */ + bus_instance->max_endpoints = 1; + bus_instance->maxpacketsize = 64; + bus_instance->serial_number_str = serial_number; + + /* configuration instance */ + memset(config_instance, 0, sizeof(struct usb_configuration_instance)); + config_instance->interfaces = NUM_INTERFACES; + config_instance->configuration_descriptor = + (struct usb_configuration_descriptor *)&fbt_config_desc; + config_instance->interface_instance_array = interface_instance; + + /* XXX: is alternate instance required in case of no alternate ? */ + /* interface instance */ + memset(interface_instance, 0, sizeof(struct usb_interface_instance)); + interface_instance->alternates = 1; + interface_instance->alternates_instance_array = alternate_instance; + + /* alternates instance */ + memset(alternate_instance, 0, sizeof(struct usb_alternate_instance)); + alternate_instance->interface_descriptor = interface_descriptors; + alternate_instance->endpoints = NUM_ENDPOINTS; + alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs; + + /* endpoint instances */ + memset(endpoint_instance, 0, sizeof(endpoint_instance)); + endpoint_instance[0].endpoint_address = 0; + endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE; + endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL; + endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE; + endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL; + /* XXX: following statement to done along with other endpoints + at another place ? */ + udc_setup_ep(device_instance, 0, &endpoint_instance[0]); + + for (i = 1; i <= NUM_ENDPOINTS; i++) { + endpoint_instance[i].endpoint_address = + ep_descriptor_ptrs[i - 1]->bEndpointAddress; + + endpoint_instance[i].rcv_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + endpoint_instance[i].rcv_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + endpoint_instance[i].tx_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + urb_link_init(&endpoint_instance[i].rcv); + urb_link_init(&endpoint_instance[i].rdy); + urb_link_init(&endpoint_instance[i].tx); + urb_link_init(&endpoint_instance[i].done); + + if (endpoint_instance[i].endpoint_address & USB_DIR_IN) + endpoint_instance[i].tx_urb = + usbd_alloc_urb(device_instance, + &endpoint_instance[i]); + else + endpoint_instance[i].rcv_urb = + usbd_alloc_urb(device_instance, + &endpoint_instance[i]); + } +} + +/* XXX: ep_descriptor_ptrs can be removed by making better use of + fbt_config_desc.endpoint_desc */ +static void fbt_init_endpoint_ptrs(void) +{ + ep_descriptor_ptrs[0] = &fbt_config_desc.endpoint_desc[0]; + ep_descriptor_ptrs[1] = &fbt_config_desc.endpoint_desc[1]; +} + +static void fbt_init_endpoints(void) +{ + int i; + + /* XXX: should it be moved to some other function ? */ + bus_instance->max_endpoints = NUM_ENDPOINTS + 1; + + /* XXX: is this for loop required ?, yes for MUSB it is */ + for (i = 1; i <= NUM_ENDPOINTS; i++) { + + /* configure packetsize based on HS negotiation status */ + if (device_instance->speed == USB_SPEED_FULL) { + FBTINFO("setting up FS USB device ep%x\n", + endpoint_instance[i].endpoint_address); + ep_descriptor_ptrs[i - 1]->wMaxPacketSize = + CONFIG_USBD_FASTBOOT_BULK_PKTSIZE_FS; + } else if (device_instance->speed == USB_SPEED_HIGH) { + FBTINFO("setting up HS USB device ep%x\n", + endpoint_instance[i].endpoint_address); + ep_descriptor_ptrs[i - 1]->wMaxPacketSize = + CONFIG_USBD_FASTBOOT_BULK_PKTSIZE_HS; + } + + endpoint_instance[i].tx_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + endpoint_instance[i].rcv_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + + udc_setup_ep(device_instance, i, &endpoint_instance[i]); + } +} + +static struct urb *next_urb(struct usb_device_instance *device, + struct usb_endpoint_instance *endpoint) +{ + struct urb *current_urb; + int space; + + /* If there's a queue, then we should add to the last urb */ + if (!endpoint->tx_queue) + current_urb = endpoint->tx_urb; + else { + /* Last urb from tx chain */ + current_urb = p2surround(struct urb, link, endpoint->tx.prev); + } + + /* Make sure this one has enough room */ + space = current_urb->buffer_length - current_urb->actual_length; + if (space > 0) + return current_urb; + else { /* No space here */ + /* First look at done list */ + current_urb = first_urb_detached(&endpoint->done); + if (!current_urb) + current_urb = usbd_alloc_urb(device, endpoint); + + urb_append(&endpoint->tx, current_urb); + endpoint->tx_queue++; + } + return current_urb; +} + +static void fbt_wait_usb_fifo_flush(void) +{ + /* give time to flush FIFO and remote to receive data. + * otherwise, USB can get hung. someday we might actually + * try checking USB fifo status directly but for now, just + * spin for some time. + */ + udelay(USB_FLUSH_DELAY_MICROSECS); +} + +/* FASTBOOT specific */ + +/* + * Android style flash utilties + */ +static void set_serial_number(const char *serial_no) +{ + strncpy(serial_number, serial_no, sizeof(serial_number)); + serial_number[sizeof(serial_number) - 1] = '\0'; + priv.serial_no = serial_number; + printf("fastboot serial_number = %s\n", serial_number); +} + +static void create_serial_number(void) +{ + char *dieid = getenv("fbt_id#"); + + if (dieid == NULL) + dieid = getenv("dieid#"); + + if (dieid == NULL) { + printf("Setting serial number from constant (no dieid info)\n"); + set_serial_number("00123"); + } else { + printf("Setting serial number from unique id\n"); + set_serial_number(dieid); + } +} + +static int is_env_partition(disk_partition_t *ptn) +{ + return !strcmp((char *)ptn->name, CONFIG_ENV_BLK_PARTITION); +} +static int is_info_partition(disk_partition_t *ptn) +{ + return !strcmp((char *)ptn->name, CONFIG_INFO_PARTITION); +} + +void fbt_add_ptn(disk_partition_t *ptn) +{ + if (pcount < MAX_PTN) { + memcpy(ptable + pcount, ptn, sizeof(*ptn)); + pcount++; + } +} + +static int fbt_load_partition_table(void) +{ + disk_partition_t *info_ptn; + unsigned int i; + + if (board_fbt_load_ptbl()) { + printf("board_fbt_load_ptbl() failed\n"); + return -1; + } + + /* load device info partition if it exists */ + info_ptn = fastboot_flash_find_ptn(CONFIG_INFO_PARTITION); + if (info_ptn) { + struct info_partition_header *info_header; + char *name, *next_name; + char *value; + + lbaint_t num_blks = 1; + i = partition_read_blks(priv.dev_desc, info_ptn, + &num_blks, priv.transfer_buffer); + if (i) { + printf("failed to read info partition. error=%d\n", i); + goto no_existing_info; + } + + /* parse the info partition read from the device */ + info_header = + (struct info_partition_header *)priv.transfer_buffer; + name = (char *)(info_header + 1); + value = name; + + if (memcmp(&info_header->magic, info_partition_magic, + sizeof(info_partition_magic)) != 0) { + printf("info partition magic 0x%x invalid," + " assuming none\n", info_header->magic); + goto no_existing_info; + } + if (info_header->num_values > FASTBOOT_MAX_NUM_DEVICE_INFO) { + printf("info partition num values %d too large " + " (max %d)\n", info_header->num_values, + FASTBOOT_MAX_NUM_DEVICE_INFO); + goto no_existing_info; + } + priv.num_device_info = info_header->num_values; + /* the name/value pairs are in the format: + * name1=value1\n + * name2=value2\n + * this makes it easier to read if we dump the partition + * to a file + */ + printf("%d device info entries read from %s partition:\n", + priv.num_device_info, info_ptn->name); + for (i = 0; i < priv.num_device_info; i++) { + while (*value != '=') + value++; + *value++ = '\0'; + next_name = value; + while (*next_name != '\n') + next_name++; + *next_name++ = '\0'; + priv.dev_info[i].name = strdup(name); + priv.dev_info[i].value = strdup(value); + printf("\t%s=%s\n", priv.dev_info[i].name, + priv.dev_info[i].value); + /* initialize serial number from device info */ + if (!strcmp(name, FASTBOOT_SERIALNO_BOOTARG)) + set_serial_number(value); + name = next_name; + } + priv.dev_info_uninitialized = 0; + } else { +no_existing_info: + priv.dev_info_uninitialized = 1; + printf("No existing device info found.\n"); + } + + if (priv.serial_no == NULL) + create_serial_number(); + + return 0; +} + +static disk_partition_t *fastboot_flash_find_ptn(const char *name) +{ + unsigned int n; + + if (pcount == 0) { + if (fbt_load_partition_table()) { + printf("Unable to load partition table, aborting\n"); + return NULL; + } + } + + for (n = 0; n < pcount; n++) + if (!strcmp((char *)ptable[n].name, name)) + return ptable + n; + return NULL; +} + +void fbt_reset_ptn(void) +{ + pcount = 0; + if (fbt_load_partition_table()) + FBTERR("Unable to load partition table\n"); +} + +static void fbt_set_unlocked(int unlocked) +{ + char *unlocked_string; + + printf("Setting device to %s\n", + unlocked ? "unlocked" : "locked"); + priv.unlocked = unlocked; + if (unlocked) + unlocked_string = "1"; + else + unlocked_string = "0"; + setenv(FASTBOOT_UNLOCKED_ENV_NAME, unlocked_string); +#if defined(CONFIG_CMD_SAVEENV) + saveenv(); +#endif +} + +static void fbt_fastboot_init(void) +{ + char *fastboot_unlocked_env; + priv.flag = 0; + priv.d_size = 0; + priv.d_bytes = 0; + priv.u_size = 0; + priv.u_bytes = 0; + priv.exit = 0; + priv.unlock_pending_start_time = 0; + + priv.unlocked = 1; + fastboot_unlocked_env = getenv(FASTBOOT_UNLOCKED_ENV_NAME); + if (fastboot_unlocked_env) { + unsigned long unlocked; + if (!strict_strtoul(fastboot_unlocked_env, 10, &unlocked)) { + if (unlocked) + priv.unlocked = 1; + else + priv.unlocked = 0; + } else { + printf("bad env setting %s of %s," + " initializing to locked\n", + fastboot_unlocked_env, + FASTBOOT_UNLOCKED_ENV_NAME); + fbt_set_unlocked(0); + } + } else { + printf("no existing env setting for %s\n", + FASTBOOT_UNLOCKED_ENV_NAME); + printf("creating one set to false\n"); + fbt_set_unlocked(0); + } + if (priv.unlocked) + printf("Device is unlocked\n"); + else + printf("Device is locked\n"); + + priv.dev_desc = get_dev_by_name(FASTBOOT_BLKDEV); + if (!priv.dev_desc) { + FBTERR("%s: fastboot device %s not found\n", + __func__, FASTBOOT_BLKDEV); + return; + } + + /* + * We need to be able to run fastboot even if there isn't a partition + * table (so we can use "oem format") and fbt_load_partition_table + * already printed an error, so just ignore the error return. + */ + (void)fbt_load_partition_table(); +} + +static void fbt_handle_erase(char *cmdbuf) +{ + disk_partition_t *ptn; + int err; + char *partition_name = cmdbuf + 6; + char *num_blocks_str; + lbaint_t num_blocks; + lbaint_t *num_blocks_p = NULL; + + /* see if there is an optional num_blocks after the partition name */ + num_blocks_str = strchr(partition_name, ' '); + if (num_blocks_str) { + /* null terminate the partition name */ + *num_blocks_str = 0; + num_blocks_str++; + num_blocks = simple_strtoull(num_blocks_str, NULL, 10); + num_blocks_p = &num_blocks; + } + + ptn = fastboot_flash_find_ptn(partition_name); + if (ptn == 0) { + printf("Partition %s does not exist\n", partition_name); + sprintf(priv.response, "FAILpartition does not exist"); + return; + } + +#ifndef CONFIG_MFG + /* don't allow erasing environment partition or a valid + * device info partition in a production u-boot */ + if (is_env_partition(ptn) || + (is_info_partition(ptn) && (!priv.dev_info_uninitialized))) { + printf("Not allowed to erase %s partition\n", ptn->name); + strcpy(priv.response, "FAILnot allowed to erase partition"); + return; + } +#endif + + printf("Erasing partition '%s':\n", ptn->name); + + printf("\tstart blk %lu, blk_cnt %lu of %lu\n", ptn->start, + num_blocks_p ? num_blocks : ptn->size, ptn->size); + + err = partition_erase_blks(priv.dev_desc, ptn, num_blocks_p); + if (err) { + printf("Erasing '%s' FAILED! error=%d\n", ptn->name, err); + sprintf(priv.response, + "FAILfailed to erase partition (%d)", err); + } else { + printf("partition '%s' erased\n", ptn->name); + sprintf(priv.response, "OKAY"); + } +} + +#define SPARSE_HEADER_MAJOR_VER 1 + +static int _unsparse(unsigned char *source, + lbaint_t sector, lbaint_t num_blks) +{ + sparse_header_t *header = (void *) source; + u32 i; + unsigned long blksz = priv.dev_desc->blksz; + u64 section_size = (u64)num_blks * blksz; + u64 outlen = 0; + + FBTINFO("sparse_header:\n"); + FBTINFO("\t magic=0x%08X\n", header->magic); + FBTINFO("\t version=%u.%u\n", header->major_version, + header->minor_version); + FBTINFO("\t file_hdr_size=%u\n", header->file_hdr_sz); + FBTINFO("\tchunk_hdr_size=%u\n", header->chunk_hdr_sz); + FBTINFO("\t blk_sz=%u\n", header->blk_sz); + FBTINFO("\t total_blks=%u\n", header->total_blks); + FBTINFO("\t total_chunks=%u\n", header->total_chunks); + FBTINFO("\timage_checksum=%u\n", header->image_checksum); + + if (header->magic != SPARSE_HEADER_MAGIC) { + printf("sparse: bad magic\n"); + return 1; + } + + if (((u64)header->total_blks * header->blk_sz) > section_size) { + printf("sparse: section size %llu MB limit: exceeded\n", + section_size/(1024*1024)); + return 1; + } + + if ((header->major_version != SPARSE_HEADER_MAJOR_VER) || + (header->file_hdr_sz != sizeof(sparse_header_t)) || + (header->chunk_hdr_sz != sizeof(chunk_header_t))) { + printf("sparse: incompatible format\n"); + return 1; + } + + /* Skip the header now */ + source += header->file_hdr_sz; + + for (i = 0; i < header->total_chunks; i++) { + u64 clen = 0; + lbaint_t blkcnt; + chunk_header_t *chunk = (void *) source; + + FBTINFO("chunk_header:\n"); + FBTINFO("\t chunk_type=%u\n", chunk->chunk_type); + FBTINFO("\t chunk_sz=%u\n", chunk->chunk_sz); + FBTINFO("\t total_sz=%u\n", chunk->total_sz); + /* move to next chunk */ + source += sizeof(chunk_header_t); + + switch (chunk->chunk_type) { + case CHUNK_TYPE_RAW: + clen = (u64)chunk->chunk_sz * header->blk_sz; + FBTINFO("sparse: RAW blk=%d bsz=%d:" + " write(sector=%lu,clen=%llu)\n", + chunk->chunk_sz, header->blk_sz, sector, clen); + + if (chunk->total_sz != (clen + sizeof(chunk_header_t))) { + printf("sparse: bad chunk size for" + " chunk %d, type Raw\n", i); + return 1; + } + + outlen += clen; + if (outlen > section_size) { + printf("sparse: section size %llu MB limit:" + " exceeded\n", section_size/(1024*1024)); + return 1; + } + blkcnt = clen / blksz; + FBTDBG("sparse: RAW blk=%d bsz=%d:" + " write(sector=%lu,clen=%llu)\n", + chunk->chunk_sz, header->blk_sz, sector, clen); + if (priv.dev_desc->block_write(priv.dev_desc->dev, + sector, blkcnt, source) + != blkcnt) { + printf("sparse: block write to sector %lu" + " of %llu bytes (%ld blkcnt) failed\n", + sector, clen, blkcnt); + return 1; + } + + sector += (clen / blksz); + source += clen; + break; + + case CHUNK_TYPE_DONT_CARE: + if (chunk->total_sz != sizeof(chunk_header_t)) { + printf("sparse: bogus DONT CARE chunk\n"); + return 1; + } + clen = (u64)chunk->chunk_sz * header->blk_sz; + FBTDBG("sparse: DONT_CARE blk=%d bsz=%d:" + " skip(sector=%lu,clen=%llu)\n", + chunk->chunk_sz, header->blk_sz, sector, clen); + + outlen += clen; + if (outlen > section_size) { + printf("sparse: section size %llu MB limit:" + " exceeded\n", section_size/(1024*1024)); + return 1; + } + sector += (clen / blksz); + break; + + default: + printf("sparse: unknown chunk ID %04x\n", + chunk->chunk_type); + return 1; + } + } + + printf("sparse: out-length %llu MB\n", outlen/(1024*1024)); + return 0; +} + +static int do_unsparse(disk_partition_t *ptn, unsigned char *source, + lbaint_t sector, lbaint_t num_blks) +{ + int rtn; + if (partition_write_pre(ptn)) + return 1; + + rtn = _unsparse(source, sector, num_blks); + + if (partition_write_post(ptn)) + return 1; + + return rtn; +} + +static int fbt_save_info(disk_partition_t *info_ptn) +{ + struct info_partition_header *info_header; + char *name; + char *value; + int i; + + if (!priv.dev_info_uninitialized) { + printf("%s partition already initialized, " + " cannot write to it again\n", info_ptn->name); + return -1; + } + + info_header = (struct info_partition_header *)priv.transfer_buffer; + name = (char *)(info_header + 1); + memset(info_header, 0, priv.dev_desc->blksz); + memcpy(&info_header->magic, info_partition_magic, + sizeof(info_partition_magic)); + info_header->num_values = priv.num_device_info; + + for (i = 0; i < priv.num_device_info; i++) { + unsigned int len = strlen(priv.dev_info[i].name); + memcpy(name, priv.dev_info[i].name, len); + value = name + len; + *value++ = '='; + if (priv.dev_info[i].value) { + len = strlen(priv.dev_info[i].value); + memcpy(value, priv.dev_info[i].value, len); + name = value + len; + *name++ = '\n'; + } + } + lbaint_t num_blks = 1; + i = partition_write_blks(priv.dev_desc, info_ptn, &num_blks, + info_header); + if (i) { + printf("block write to sector %lu failed, error=%d", + info_ptn->start, i); + return -1; + } + priv.dev_info_uninitialized = 0; + return 0; +} + +static void fbt_handle_flash(char *cmdbuf, int check_unlock) +{ + disk_partition_t *ptn; + + if (check_unlock && !priv.unlocked) { + printf("%s: failed, device is locked\n", __func__); + sprintf(priv.response, "FAILdevice is locked"); + return; + } + + if (!priv.d_bytes) { + printf("%s: failed, no image downloaded\n", __func__); + sprintf(priv.response, "FAILno image downloaded"); + return; + } + + ptn = fastboot_flash_find_ptn(cmdbuf + 6); + if (ptn == 0) { + printf("%s: failed, partition %s does not exist\n", + __func__, cmdbuf + 6); + sprintf(priv.response, "FAILpartition does not exist"); + return; + } + + /* do board/cpu specific special handling if needed. this + * can include modifying priv.image_start_ptr to flash from + * an address other than the start of the transfer buffer. + */ + priv.image_start_ptr = priv.transfer_buffer; + if (board_fbt_handle_flash(ptn, &priv)) { + /* error case, return. expect priv.response to be + * set by the board specific handler. + */ + printf("%s: failed, board_fbt_handle_flash() error\n", + __func__); + return; + } + + /* Prevent using flash command to write to device_info partition */ + if (is_info_partition(ptn)) { + printf("%s: failed, partition not writable" + " using flash command\n", + __func__); + sprintf(priv.response, + "FAILpartition not writable using flash command"); + return; + } + + /* Check if this is not really a flash write but rather a saveenv */ + if (is_env_partition(ptn)) { + if (!himport_r(&env_htab, + (const char *)priv.image_start_ptr, + priv.d_bytes, '\n', H_NOCLEAR)) { + FBTINFO("Import '%s' FAILED!\n", ptn->name); + sprintf(priv.response, "FAIL: Import environment"); + return; + } + +#if defined(CONFIG_CMD_SAVEENV) + if (saveenv()) { + printf("Writing '%s' FAILED!\n", ptn->name); + sprintf(priv.response, "FAIL: Write partition"); + return; + } + printf("saveenv to '%s' DONE!\n", ptn->name); +#endif + sprintf(priv.response, "OKAY"); + } else { + /* Normal case */ + printf("writing to partition '%s'\n", ptn->name); + + /* Check if we have sparse compressed image */ + if (((sparse_header_t *)priv.image_start_ptr)->magic + == SPARSE_HEADER_MAGIC) { + printf("fastboot: %s is in sparse format\n", ptn->name); + if (!do_unsparse(ptn, priv.image_start_ptr, + ptn->start, ptn->size)) { + printf("Writing sparsed: '%s' DONE!\n", + ptn->name); + sprintf(priv.response, "OKAY"); + } else { + printf("Writing sparsed '%s' FAILED!\n", + ptn->name); + sprintf(priv.response, "FAIL: Sparsed Write"); + } + } else { + /* Normal image: no sparse */ + int err; + loff_t num_bytes = priv.d_bytes; + + printf("Writing %llu bytes to '%s'\n", + num_bytes, ptn->name); + err = partition_write_bytes(priv.dev_desc, ptn, + &num_bytes, priv.image_start_ptr); + if (err) { + printf("Writing '%s' FAILED! error=%d\n", + ptn->name, err); + sprintf(priv.response, + "FAILWrite partition, error=%d", err); + } else { + printf("Writing '%s' DONE!\n", ptn->name); + sprintf(priv.response, "OKAY"); + } + } + } /* Normal Case */ +} + +struct getvar_entry { + const char *variable_name; + int exact_match; + const char *(*getvar_func)(const char *arg); +}; + +static const char *getvar_version(const char *unused) +{ + return version_string; +} + +static const char *getvar_version_baseband(const char *unused) +{ + return "n/a"; +} + +static const char *getvar_version_bootloader(const char *unused) +{ + return CONFIG_FASTBOOT_VERSION_BOOTLOADER; +} + +static const char *getvar_unlocked(const char *unused) +{ + return (priv.unlocked ? "yes" : "no"); +} + +static const char *getvar_secure(const char *unused) +{ + /* we use the inverse meaning of unlocked */ + return (priv.unlocked ? "no" : "yes"); +} + +#if defined (CONFIG_OMAP) +static const char *getvar_device_type(const char *unused) +{ + switch(get_device_type()) { + case TST_DEVICE: + return "TST"; + case EMU_DEVICE: + return "EMU"; + case HS_DEVICE: + return "HS"; + case GP_DEVICE: + return "GP"; + default: + return "unknown"; + } +} +#endif + +static const char *getvar_product(const char *unused) +{ + return FASTBOOT_PRODUCT_NAME; +} + +static const char *getvar_serialno(const char *unused) +{ + return priv.serial_no; +} + +static const char *getvar_partition_type(const char *args) +{ + const char *partition_name; + const char *type; + + if (!strcmp(args, "all")) { + int i; + for (i = 0; i < pcount; i++) { + partition_name = (char*)ptable[i].name; + printf("partition \"%s\" has type \"%s\"\n", + partition_name, + board_fbt_get_partition_type(partition_name)); + } + return NULL; + } + + partition_name = args + sizeof("partition-type:") - 1; + type = board_fbt_get_partition_type(partition_name); + if (type) { + return type; + } + snprintf(priv.response, sizeof(priv.response), + "FAILunknown partition %s\n", partition_name); + return NULL; +} + +static const char *getvar_partition_size(const char *args) +{ + const char *partition_name; + disk_partition_t *ptn; + + if (!strcmp(args, "all")) { + int i; + for (i = 0; i < pcount; i++) { + printf("partition \"%s\" has size 0x%016llx bytes\n", + ptable[i].name, + (uint64_t)ptable[i].size * ptable[i].blksz); + } + return NULL; + } + + partition_name = args + sizeof("partition-size:") - 1; + ptn = fastboot_flash_find_ptn(partition_name); + if (ptn) { + snprintf(priv.response, sizeof(priv.response), + "OKAY0x%016llx", (uint64_t)ptn->size * ptn->blksz); + } else { + snprintf(priv.response, sizeof(priv.response), + "FAILunknown partition %s\n", partition_name); + } + return NULL; +} + +static const struct getvar_entry getvar_table[] = { + {"version", 1, getvar_version}, + {"version-baseband", 1, getvar_version_baseband}, + {"version-bootloader", 1, getvar_version_bootloader}, + {"unlocked", 1, getvar_unlocked}, + {"secure", 1, getvar_secure}, +#if defined (CONFIG_OMAP) + {"device_type", 1, getvar_device_type}, +#endif + {"product", 1, getvar_product}, + {"serialno", 1, getvar_serialno}, + {"partition-type:", 0, getvar_partition_type}, + {"partition-size:", 0, getvar_partition_size} +}; + +static void fbt_handle_getvar(char *cmdbuf) +{ + char *subcmd = cmdbuf + sizeof("getvar:") - 1; + const char *value = NULL; + int do_all; + int i; + if (!strcmp(subcmd, "all")) + do_all = 1; + else + do_all = 0; + + if (do_all) { + for (i = 0; i < ARRAY_SIZE(getvar_table); i++) { + value = (getvar_table[i].getvar_func)(subcmd); + if (value ) { + printf("%s: %s\n", + getvar_table[i].variable_name, value); + } + } + strcpy(priv.response, "OKAY"); + } else { + for (i = 0; i < ARRAY_SIZE(getvar_table); i++) { + int match; + if (getvar_table[i].exact_match) { + /* look for exact string match */ + match = !strcmp(getvar_table[i].variable_name, + subcmd); + } else { + /* look for the target string at the + * beginning of the argument passed + */ + match = strstr(subcmd, + getvar_table[i].variable_name) == + subcmd; + } + if (match) { + value = (getvar_table[i].getvar_func)(subcmd); + if (value == NULL) { + /* handler did it all in terms of + * creating a response. + */ + return; + } + /* fall through to let the common code + * handle creating a response string. + */ + break; + } + } +#ifdef CONFIG_FASTBOOT_UBOOT_GETVAR + if (!value) { + ENTRY e, *ep; + + e.key = subcmd; + e.data = NULL; + ep = NULL; + if (hsearch_r(e, FIND, &ep, &env_htab) && ep != NULL) + value = ep->data; + } +#endif + if (value) { + /* At first I was reluctant to use strncpy because it + * typically pads the whole buffer with nulls, but + * U-Boot's strncpy does not do that. However, I + * do rely on priv.null_term after priv.response + * in the struct cmd_fastboot_interface to ensure + * the strlen in fbt_response_process doesn't take + * a long time. + */ + strcpy(priv.response, "OKAY"); + strncpy(priv.response + 4, value, + (sizeof(priv.response) - 4)); + } else { + strcpy(priv.response, "FAILunknown variable"); + } + } +} + +static void fbt_handle_reboot(const char *cmdbuf) +{ + if (!strcmp(&cmdbuf[6], "-bootloader")) { + FBTDBG("%s\n", cmdbuf); + board_fbt_set_reboot_type(FASTBOOT_REBOOT_BOOTLOADER); + } + if (!strcmp(&cmdbuf[6], "-recovery")) { + FBTDBG("%s\n", cmdbuf); + board_fbt_set_reboot_type(FASTBOOT_REBOOT_RECOVERY); + } + if (!strcmp(&cmdbuf[6], "-recovery:wipe_data")) { + FBTDBG("%s\n", cmdbuf); + board_fbt_set_reboot_type(FASTBOOT_REBOOT_RECOVERY_WIPE_DATA); + } + + strcpy(priv.response, "OKAY"); + priv.flag |= FASTBOOT_FLAG_RESPONSE; + fbt_handle_response(); + udelay(1000000); /* 1 sec */ + + board_fbt_end(); + + do_reset(NULL, 0, 0, NULL); +} + +static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of fastboot cmdbuf */ + +static void fbt_handle_oem_setinfo(const char *cmdbuf) +{ + char *name, *value; + struct device_info *di; + + FBTDBG("oem setinfo\n"); + + /* this is only allowed if the device info isn't already + * initlialized in flash + */ + if (!priv.dev_info_uninitialized) { + printf("Not allowed to change device info already in flash\n"); + strcpy(priv.response, "FAILnot allowed to change" + " device info already in flash"); + return; + } + + if (priv.num_device_info == FASTBOOT_MAX_NUM_DEVICE_INFO) { + printf("Already at maximum number of device info (%d)," + " no more allowed\n", FASTBOOT_MAX_NUM_DEVICE_INFO); + strcpy(priv.response, "FAILmax device info reached"); + return; + } + + /* copy to tmp_buf which will be modified by str_tok() */ + strcpy(tmp_buf, cmdbuf); + + name = strtok(tmp_buf, "="); + value = strtok(NULL, "\n"); + if (!name || !value) { + printf("Invalid format for setinfo.\n"); + printf("Syntax is " + "'fastboot oem setinfo <info_name>=<info_value>\n"); + strcpy(priv.response, "FAILinvalid device info"); + return; + } + + /* we enter new value at end so last slot should be free. + * we don't currently allow changing a value already set. + */ + di = &priv.dev_info[priv.num_device_info]; + if (di->name || di->value) { + printf("Error, device info entry not free as expected\n"); + strcpy(priv.response, "FAILinternal error"); + return; + } + + di->name = strdup(name); + di->value = strdup(value); + if ((di->name == NULL) || (di->value == NULL)) { + printf("strdup() failed, unable to set info\n"); + strcpy(priv.response, "FAILstrdup() failure\n"); + free(di->name); + free(di->value); + return; + } + + printf("Set device info %s=%s\n", di->name, di->value); + if (!strcmp(di->name, FASTBOOT_SERIALNO_BOOTARG)) + set_serial_number(di->value); + priv.num_device_info++; + + strcpy(priv.response, "OKAY"); +} + +static int fbt_send_raw_info(const char *info, int bytes_left) +{ + int response_max; + + if (!priv.executing_command) + return -1; + + /* break up info into response sized chunks */ + /* remove trailing '\n' */ + if (info[bytes_left-1] == '\n') + bytes_left--; + + /* -4 for the INFO prefix */ + response_max = sizeof(priv.response) - 4; + strcpy(priv.response, "INFO"); + while (1) { + if (bytes_left >= response_max) { + strncpy(priv.response + 4, info, + response_max); + + /* flush any data set by command */ + priv.flag |= FASTBOOT_FLAG_RESPONSE; + fbt_handle_response(); + fbt_wait_usb_fifo_flush(); + + info += response_max; + bytes_left -= response_max; + } else { + strncpy(priv.response + 4, info, + bytes_left); + + /* in case we stripped '\n', + make sure priv.response is + terminated */ + priv.response[4 + bytes_left] = '\0'; + + break; + } + } + + priv.flag |= FASTBOOT_FLAG_RESPONSE; + fbt_handle_response(); + fbt_wait_usb_fifo_flush(); + + return 0; +} + +static void fbt_dump_log(char *buf, uint32_t buf_size) +{ + /* the log consists of a bunch of printf output, with + * logs of '\n' interspersed. to make it format a + * bit better when sending it via the INFO + * part of the fastboot protocol, which has a limited + * buffer, break the log into bits that end + * with '\n', like replaying the printfs. + */ + char *line_start = buf; + + if (buf_size == 0) { + printf("%s: unexpected buf size of 0\n", __func__); + return; + } + + /* guarantee null termination for strchr/strlen */ + buf[buf_size - 1] = 0; + while (buf_size) { + char *next_line = strchr(line_start, '\n'); + if (next_line) { + int len = next_line - line_start + 1; + fbt_send_raw_info(line_start, len); + line_start += len; + buf_size -= len; + } else { + fbt_send_raw_info(line_start, strlen(line_start)); + break; + } + } +} + +struct ram_console_buffer { + uint32_t sig; + uint32_t start; + uint32_t size; + uint8_t data[0]; +}; +#define RAM_CONSOLE_SIG (0x43474244) /* 'DBGC' */ + +static void fbt_dump_kmsg(void) +{ + /* the kmsg log is similar to the log we keep in the bootloader + * except that it has a header, is at an address fixed for + * each board, and starts with a ram_console_buffer structure. + */ + struct ram_console_buffer *buf; +#ifndef CONFIG_FASTBOOT_RAMCONSOLE_START + printf("No ram console start address defined\n"); + strcpy(priv.response, "FAILNo ram console start address defined"); + return; +#else + buf = (struct ram_console_buffer *)CONFIG_FASTBOOT_RAMCONSOLE_START; +#endif + if (buf->sig != RAM_CONSOLE_SIG) { + printf("Ram console signature not found\n"); + strcpy(priv.response, "FAILRam console signature not found\n"); + return; + } + printf("Ram console found (size %d, start %d):\n", + buf->size, buf->start); + fbt_dump_log((char *)&buf->data[0], buf->start); + strcpy(priv.response, "OKAY"); +} + +static void fbt_handle_oem(char *cmdbuf) +{ + cmdbuf += 4; + + /* %fastboot oem log */ + if (strcmp(cmdbuf, "log") == 0) { + FBTDBG("oem %s\n", cmdbuf); + fbt_dump_log(log_buffer, log_position); + strcpy(priv.response, "OKAY"); + return; + } + + /* %fastboot oem kmsg */ + if (strcmp(cmdbuf, "kmsg") == 0) { + FBTDBG("oem %s\n", cmdbuf); + fbt_dump_kmsg(); + return; + } + + /* %fastboot oem recovery */ + if (strcmp(cmdbuf, "recovery") == 0) { + FBTDBG("oem recovery\n"); + fbt_handle_reboot("reboot-recovery"); + return; + } + + /* %fastboot oem recovery:wipe_data */ + if (strcmp(cmdbuf, "recovery:wipe_data") == 0) { + FBTDBG("oem recovery:wipe_data\n"); + fbt_handle_reboot("reboot-recovery:wipe_data"); + return; + } + + /* %fastboot oem unlock */ + if (strcmp(cmdbuf, "unlock") == 0) { + FBTDBG("oem unlock\n"); + if (priv.unlocked) { + printf("oem unlock ignored, device already unlocked\n"); + strcpy(priv.response, "FAILalready unlocked"); + return; + } + printf("oem unlock requested:\n"); + printf("\tUnlocking forces a factory reset and could\n"); + printf("\topen your device up to a world of hurt. If you\n"); + printf("\tare sure you know what you're doing, then accept\n"); + printf("\tin %d seconds via 'fastboot oem unlock_accept'.\n", + FASTBOOT_UNLOCK_TIMEOUT_SECS); + priv.unlock_pending_start_time = get_timer(0); + strcpy(priv.response, "OKAY"); + return; + } + + if (strcmp(cmdbuf, "unlock_accept") == 0) { + int err; + FBTDBG("oem unlock_accept\n"); + if (!priv.unlock_pending_start_time) { + printf("oem unlock_accept ignored, not pending\n"); + strcpy(priv.response, "FAILoem unlock not requested"); + return; + } + priv.unlock_pending_start_time = 0; + printf("Erasing userdata partition\n"); + err = partition_erase_blks(priv.dev_desc, + fastboot_flash_find_ptn("userdata"), + NULL); + if (err) { + printf("Erase failed with error %d\n", err); + strcpy(priv.response, "FAILErasing userdata failed"); + return; + } + printf("Erasing succeeded\n"); + fbt_set_unlocked(1); + strcpy(priv.response, "OKAY"); + priv.flag |= FASTBOOT_FLAG_RESPONSE; + fbt_handle_response(); + udelay(1000000); /* 1 sec */ + /* now reboot into recovery to do a format of the + * userdata partition so it's ready to use on next boot + */ + fbt_run_recovery_wipe_data(); + return; + } + + if (strcmp(cmdbuf, "lock") == 0) { + FBTDBG("oem lock\n"); + if (!priv.unlocked) { + printf("oem lock ignored, already locked\n"); + strcpy(priv.response, "FAILalready locked"); + return; + } + fbt_set_unlocked(0); + strcpy(priv.response, "OKAY"); + return; + } + + /* %fastboot oem erase partition <numblocks> + * similar to 'fastboot erase' except an optional number + * of blocks can be passed to erase less than the + * full partition, for speed + */ + if (strncmp(cmdbuf, "erase ", 6) == 0) { + FBTDBG("oem %s\n", cmdbuf); + fbt_handle_erase(cmdbuf); + return; + } + + /* All other oem commands are not allowed if device is locked */ + if (!priv.unlocked) { + sprintf(priv.response, "FAILdevice is locked"); + return; + } + + /* %fastboot oem setinfo <info_name>=<info_value> */ + if (strncmp(cmdbuf, "setinfo ", 8) == 0) { + cmdbuf += 8; + fbt_handle_oem_setinfo(cmdbuf); + return; + } + + /* %fastboot oem saveinfo */ + if (strcmp(cmdbuf, "saveinfo") == 0) { + disk_partition_t *info_ptn; + info_ptn = fastboot_flash_find_ptn(CONFIG_INFO_PARTITION); + + if (info_ptn == NULL) { + sprintf(priv.response, "FAILpartition does not exist"); + return; + } + if (fbt_save_info(info_ptn)) { + printf("Writing '%s' FAILED!\n", info_ptn->name); + sprintf(priv.response, "FAIL: Write partition"); + } else { + printf("Device info saved to partition '%s'\n", + info_ptn->name); + sprintf(priv.response, "OKAY"); + } + return; + } + + /* %fastboot oem ucmd ... */ + if (strncmp(cmdbuf, "ucmd ", 5) == 0) { + FBTDBG("oem %s\n", cmdbuf); + cmdbuf += 5; + + if (run_command(cmdbuf, 0) < 0) + strcpy(priv.response, "FAILcommand failed"); + else + strcpy(priv.response, "OKAY"); + return; + } + + /* %fastboot oem [xxx] */ + FBTDBG("oem %s\n", cmdbuf); + if (board_fbt_oem(cmdbuf) >= 0) { + strcpy(priv.response, "OKAY"); + return; + } + + printf("\nfastboot: unsupported oem command %s\n", cmdbuf); + strcpy(priv.response, "FAILinvalid command"); +} + +static void fbt_handle_boot(const char *cmdbuf) +{ + if (!priv.unlocked) { + sprintf(priv.response, "FAILdevice is locked"); + return; + } + + if ((priv.d_bytes) && + (CONFIG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE < priv.d_bytes)) { + char start[32]; + char *booti[] = { "booti", start }; + char *go[] = { "go", start }; + + /* + * Use this later to determine if a command line was passed + * for the kernel. + */ + struct fastboot_boot_img_hdr *fb_hdr = + (struct fastboot_boot_img_hdr *) priv.transfer_buffer; + + board_fbt_end(); + + sprintf(start, "%p", fb_hdr); + + /* Execution should jump to kernel so send the response + now and wait a bit. */ + sprintf(priv.response, "OKAY"); + priv.flag |= FASTBOOT_FLAG_RESPONSE; + fbt_handle_response(); + udelay(1000000); /* 1 sec */ + + do_booti(NULL, 0, ARRAY_SIZE(booti), booti); + + printf("do_booti() returned, trying go..\n"); + + FBTINFO("Booting raw image..\n"); + do_go(NULL, 0, ARRAY_SIZE(go), go); + + FBTERR("booting failed, reset the board\n"); + board_fbt_start(); + } + sprintf(priv.response, "FAILinvalid boot image"); +} + +/* XXX: Replace magic number & strings with macros */ +static int fbt_rx_process(unsigned char *buffer, int length) +{ + struct usb_endpoint_instance *ep; + char *cmdbuf; + int clear_cmd_buf; + + if (priv.d_size) { + if (length < priv.d_size) { + /* don't clear cmd buf because we've replaced it + * with our transfer buffer. we'll clear it at + * the end of the download. + */ + return 0; + } + + /* transfer complete */ + priv.d_bytes = priv.d_size; + priv.d_size = 0; + strcpy(priv.response, "OKAY"); + priv.flag |= FASTBOOT_FLAG_RESPONSE; + + /* restore default buffer in urb */ + ep = &endpoint_instance[RX_EP_INDEX]; + ep->rcv_urb->buffer = (u8 *)ep->rcv_urb->buffer_data; + ep->rcv_urb->buffer_length = sizeof(ep->rcv_urb->buffer_data); + + FBTINFO("downloaded %llu bytes\n", priv.d_bytes); + + /* clear the cmd buf from last time */ + return 1; + } + + /* command */ + cmdbuf = (char *) buffer; + clear_cmd_buf = 1; + + /* Generic failed response */ + strcpy(priv.response, "FAIL"); + + FBTDBG("command\n"); + + printf("cmdbuf = (%s)\n", cmdbuf); + priv.executing_command = 1; + + /* %fastboot getvar: <var_name> */ + if (memcmp(cmdbuf, "getvar:", 7) == 0) { + FBTDBG("getvar\n"); + fbt_handle_getvar(cmdbuf); + } + + /* %fastboot oem <cmd> */ + else if (memcmp(cmdbuf, "oem ", 4) == 0) { + FBTDBG("oem\n"); + fbt_handle_oem(cmdbuf); + } + + /* %fastboot erase <partition_name> */ + else if (memcmp(cmdbuf, "erase:", 6) == 0) { + FBTDBG("erase\n"); + fbt_handle_erase(cmdbuf); + } + + /* %fastboot flash:<partition_name> */ + else if (memcmp(cmdbuf, "flash:", 6) == 0) { + FBTDBG("flash\n"); + fbt_handle_flash(cmdbuf, 1); + } + + /* %fastboot reboot + * %fastboot reboot-bootloader + */ + else if (memcmp(cmdbuf, "reboot", 6) == 0) { + FBTDBG("reboot or reboot-bootloader\n"); + fbt_handle_reboot(cmdbuf); + } + + /* %fastboot continue */ + else if (strcmp(cmdbuf, "continue") == 0) { + FBTDBG("continue\n"); + strcpy(priv.response, "OKAY"); + priv.exit = 1; + } + + /* %fastboot boot <kernel> [ <ramdisk> ] */ + else if (memcmp(cmdbuf, "boot", 4) == 0) { + FBTDBG("boot\n"); + fbt_handle_boot(cmdbuf); + } + + /* Sent as part of a '%fastboot flash <partname>' command + * This sends the data over with byte count: + * %download:<num_bytes> + */ + else if (memcmp(cmdbuf, "download:", 9) == 0) { + FBTDBG("download\n"); + + /* XXX: need any check for size & bytes ? */ + priv.d_size = simple_strtoul (cmdbuf + 9, NULL, 16); + priv.d_bytes = 0; + + FBTINFO("starting download of %llu bytes\n", priv.d_size); + if (priv.d_size == 0) { + strcpy(priv.response, "FAILdata invalid size"); + } else if (priv.d_size > priv.transfer_buffer_size) { + priv.d_size = 0; + strcpy(priv.response, "FAILdata too large"); + } else { + sprintf(priv.response, "DATA%08llx", priv.d_size); + + /* as an optimization, replace the builtin + * urb->buffer and urb->buffer_length with our + * own so we don't have to do extra copy. + */ + ep = &endpoint_instance[RX_EP_INDEX]; + ep->rcv_urb->buffer = priv.transfer_buffer; + ep->rcv_urb->buffer_length = priv.d_size; + ep->rcv_urb->actual_length = 0; + + /* don't poison the cmd buffer because + * we've replaced it with our + * transfer buffer for the download. + */ + clear_cmd_buf = 0; + } + } + priv.flag |= FASTBOOT_FLAG_RESPONSE; + priv.executing_command = 0; + return clear_cmd_buf; +} + +static void fbt_handle_rx(void) +{ + struct usb_endpoint_instance *ep = &endpoint_instance[RX_EP_INDEX]; + + /* XXX: Or update status field, if so, + "usbd_rcv_complete" [gadget/core.c] also need to be modified */ + if (ep->rcv_urb->actual_length) { + FBTDBG("rx length: %u\n", ep->rcv_urb->actual_length); + if (fbt_rx_process(ep->rcv_urb->buffer, + ep->rcv_urb->actual_length)) { + /* Poison the command buffer so there's no confusion + * when we receive the next one. fastboot commands + * are sent w/o NULL termination so we don't want + * stale data in the buffer. + * Also, it is assumed that at the time of creation of + * urb it is poisoned. + */ + memset(ep->rcv_urb->buffer, 0, FASTBOOT_COMMAND_SIZE); + ep->rcv_urb->actual_length = 0; + } + fbt_handle_response(); + } +} + +static void fbt_response_process(void) +{ + struct usb_endpoint_instance *ep = &endpoint_instance[TX_EP_INDEX]; + struct urb *current_urb = NULL; + unsigned char *dest = NULL; + int n; + + current_urb = next_urb(device_instance, ep); + if (!current_urb) { + FBTERR("%s: current_urb NULL", __func__); + return; + } + + dest = current_urb->buffer + current_urb->actual_length; + n = MIN(64, strlen(priv.response)); + memcpy(dest, priv.response, n); + current_urb->actual_length += n; + /* + * This FBTDBG appears to break communication when DEBUG + * is on, so comment it out. + FBTDBG("response urb length: %u\n", current_urb->actual_length); + */ + if (ep->last == 0) + udc_endpoint_write(ep); +} + +static void fbt_handle_response(void) +{ + if (priv.flag & FASTBOOT_FLAG_RESPONSE) { + fbt_response_process(); + priv.flag &= ~FASTBOOT_FLAG_RESPONSE; + } +} + +static void fbt_clear_recovery_flag(void) +{ + if (getenv(FASTBOOT_RUN_RECOVERY_ENV_NAME)) { + setenv(FASTBOOT_RUN_RECOVERY_ENV_NAME, NULL); +#if defined(CONFIG_CMD_SAVEENV) + saveenv(); +#endif + } +} + +static void fbt_run_recovery(int do_saveenv) +{ + board_fbt_end(); + + /* to make recovery (which processes OTAs) more failsafe, + * we save the fact that we were asked to boot into + * recovery. if power is pulled and then restored, we + * will use that info to rerun recovery again and try + * to complete the OTA installation. + */ + if (do_saveenv) { + setenv(FASTBOOT_RUN_RECOVERY_ENV_NAME, "1"); +#ifdef CONFIG_CMD_SAVEENV + saveenv(); +#endif + } + + char *const boot_recovery_cmd[] = {"booti", "recovery"}; + do_booti(NULL, 0, ARRAY_SIZE(boot_recovery_cmd), boot_recovery_cmd); + + /* returns if recovery.img is bad */ + board_fbt_start(); + printf("\nfastboot: Error: Invalid recovery img\n"); + + /* Always clear so we don't wind up rebooting again into + * bad recovery img. + */ + fbt_clear_recovery_flag(); +} + +struct bootloader_message { + char command[32]; + char status[32]; + char recovery[1024]; +}; + +static void fbt_run_recovery_wipe_data(void) +{ + struct bootloader_message *bmsg; + + printf("Rebooting into recovery to do wipe_data\n"); + + bmsg = (struct bootloader_message*)priv.transfer_buffer; + memset(bmsg, 0, sizeof(*bmsg)); + bmsg->command[0] = 0; + bmsg->status[0] = 0; + strcpy(bmsg->recovery, "recovery\n--wipe_data"); + priv.d_bytes = sizeof(*bmsg); + + /* write this structure to the "misc" partition, no unlock check */ + fbt_handle_flash("flash:misc", 0); + + /* now reboot to recovery */ + fbt_run_recovery(1); +} + +/* + * default board-specific hooks and defaults + */ +static int __def_fbt_oem(const char *cmdbuf) +{ + return -1; +} +static void __def_fbt_set_reboot_type(enum fbt_reboot_type fre) +{ +} +static enum fbt_reboot_type __def_fbt_get_reboot_type(void) +{ + return FASTBOOT_REBOOT_NORMAL; +} +static int __def_fbt_key_pressed(void) +{ + return 0; +} +static enum fbt_reboot_type __def_fbt_key_command(void) +{ + return FASTBOOT_REBOOT_NONE; +} +static int __def_fbt_load_ptbl(void) +{ + u64 length; + disk_partition_t ptn; + int n; + int res = -1; + block_dev_desc_t *blkdev = priv.dev_desc; + unsigned long blksz = blkdev->blksz; + + init_part(blkdev); + if (blkdev->part_type == PART_TYPE_UNKNOWN) { + printf("unknown partition table on %s\n", FASTBOOT_BLKDEV); + return -1; + } + + printf("lba size = %lu\n", blksz); + printf("lba_start partition_size name\n"); + printf("========= ====================== ==============\n"); + for (n = CONFIG_MIN_PARTITION_NUM; n <= CONFIG_MAX_PARTITION_NUM; n++) { + if (get_partition_info(blkdev, n, &ptn)) + continue; /* No partition <n> */ + if (!ptn.size || !ptn.blksz || !ptn.name[0]) + continue; /* Partition <n> is empty (or sick) */ + fbt_add_ptn(&ptn); + + length = (u64)blksz * ptn.size; + if (length > (1024 * 1024)) + printf(" %8lu %12llu(%7lluM) %s\n", + ptn.start, + length, length/(1024*1024), + ptn.name); + else + printf(" %8lu %12llu(%7lluK) %s\n", + ptn.start, + length, length/1024, + ptn.name); + res = 0; + } + printf("========= ====================== ==============\n"); + return res; +} +static void __def_fbt_start(void) +{ +} +static void __def_fbt_end(void) +{ +} +static void __def_board_fbt_finalize_bootargs(char* args, size_t buf_sz) +{ + return; +} +static int __def_board_fbt_handle_flash(disk_partition_t *ptn, + struct cmd_fastboot_interface *priv) +{ + return 0; +} +static const char *__def_board_fbt_get_partition_type(const char *p_name) +{ + return NULL; +} + +int board_fbt_oem(const char *cmdbuf) + __attribute__((weak, alias("__def_fbt_oem"))); +void board_fbt_set_reboot_type(enum fbt_reboot_type fre) + __attribute__((weak, alias("__def_fbt_set_reboot_type"))); +enum fbt_reboot_type board_fbt_get_reboot_type(void) + __attribute__((weak, alias("__def_fbt_get_reboot_type"))); +int board_fbt_key_pressed(void) + __attribute__((weak, alias("__def_fbt_key_pressed"))); +enum fbt_reboot_type board_fbt_key_command(void) + __attribute__((weak, alias("__def_fbt_key_command"))); +int board_fbt_load_ptbl(void) + __attribute__((weak, alias("__def_fbt_load_ptbl"))); +void board_fbt_start(void) + __attribute__((weak, alias("__def_fbt_start"))); +void board_fbt_end(void) + __attribute__((weak, alias("__def_fbt_end"))); +void board_fbt_finalize_bootargs(char* args, size_t buf_sz) + __attribute__((weak, alias("__def_board_fbt_finalize_bootargs"))); +int board_fbt_handle_flash(disk_partition_t *ptn, + struct cmd_fastboot_interface *priv) + __attribute__((weak, alias("__def_board_fbt_handle_flash"))); +const char *board_fbt_get_partition_type(const char *partition_name) + __attribute__((weak, alias("__def_board_fbt_get_partition_type"))); + +/* command */ +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int ret; + + if (!priv.dev_desc) { + printf("fastboot was not successfully initialized\n"); + return -1; + } + + /* currently we don't allow restarting fastboot if you've run + * it before and exited to u-boot prompt. it's possible to + * support, but there's are edge cases that we're not + * sure the answer to (e.g. do we reload the partition table + * or not) and it's just not a common situation so we're + * just saying no for now. + */ + if (priv.flag & FASTBOOT_FLAG_HAS_RUN) { + printf("fastboot can't be restarted\n"); + return -1; + } + priv.flag |= FASTBOOT_FLAG_HAS_RUN; + + printf("Starting fastboot protocol\n"); + + board_fbt_start(); + + fbt_init_endpoint_ptrs(); + + ret = udc_init(); + if (ret < 0) { + FBTERR("%s: MUSB UDC init failure\n", __func__); + goto out; + } + + fbt_init_strings(); + fbt_init_instances(); + + udc_startup_events(device_instance); + udc_connect(); + + FBTINFO("fastboot initialized\n"); + + while (1) { + udc_irq(); + if (priv.configured) { + fbt_handle_rx(); + if (priv.unlock_pending_start_time) { + /* check if unlock pending should expire */ + if (get_timer(priv.unlock_pending_start_time) > + (FASTBOOT_UNLOCK_TIMEOUT_SECS * 1000)) { + printf("unlock pending expired\n"); + priv.unlock_pending_start_time = 0; + } + } + } + priv.exit |= ctrlc(); + if (priv.exit) { + FBTINFO("fastboot end\n"); + break; + } + switch(board_fbt_key_command()) { + case FASTBOOT_REBOOT_NORMAL: + printf("rebooting due to key\n"); + fbt_handle_reboot("reboot"); + break; + case FASTBOOT_REBOOT_BOOTLOADER: + printf("rebooting to bootloader due to key\n"); + fbt_handle_reboot("reboot-bootloader"); + break; + case FASTBOOT_REBOOT_RECOVERY_WIPE_DATA: + fbt_run_recovery_wipe_data(); + break; + case FASTBOOT_REBOOT_RECOVERY: + printf("starting recovery due to key\n"); + fbt_run_recovery(1); + break; + case FASTBOOT_REBOOT_UNKNOWN: + case FASTBOOT_REBOOT_NONE: + default: + break; + } + } + +out: + board_fbt_end(); + return ret; +} + +U_BOOT_CMD(fastboot, 1, 1, do_fastboot, + "use USB Fastboot protocol", NULL); + +/* Section for Android bootimage format support + * Refer: + * http://android.git.kernel.org/?p=platform/system/core.git;a=blob;f=mkbootimg/bootimg.h + */ +static void bootimg_print_image_hdr(struct fastboot_boot_img_hdr *hdr) +{ +#ifdef DEBUG + int i; + printf(" Image magic: %s\n", hdr->magic); + + printf(" kernel_size: 0x%x\n", hdr->kernel_size); + printf(" kernel_addr: 0x%x\n", hdr->kernel_addr); + + printf(" rdisk_size: 0x%x\n", hdr->ramdisk_size); + printf(" rdisk_addr: 0x%x\n", hdr->ramdisk_addr); + + printf(" second_size: 0x%x\n", hdr->second_size); + printf(" second_addr: 0x%x\n", hdr->second_addr); + + printf(" tags_addr: 0x%x\n", hdr->tags_addr); + printf(" page_size: 0x%x\n", hdr->page_size); + + printf(" name: %s\n", hdr->name); + printf(" cmdline: %s\n", hdr->cmdline); + + for (i = 0; i < 8; i++) + printf(" id[%d]: 0x%x\n", i, hdr->id[i]); +#endif +} + +/* booti [ <addr> | <partition> ] */ +static int do_booti(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *boot_source = "boot"; + block_dev_desc_t *blkdev = priv.dev_desc; + disk_partition_t *ptn; + struct fastboot_boot_img_hdr *hdr = NULL; + bootm_headers_t images; + int need_post_ran = 0; + + if (argc >= 2) + boot_source = argv[1]; + + if (!blkdev) { + printf("fastboot was not successfully initialized\n"); + return -1; + } + + ptn = fastboot_flash_find_ptn(boot_source); + if (ptn) { + unsigned long blksz; + unsigned sector; + unsigned blocks; + + if (partition_read_pre(ptn)) { + printf("pre-read commands for partition '%s' failed\n", + ptn->name); + goto fail; + } + need_post_ran = 1; + + blksz = blkdev->blksz; + hdr = malloc(blksz); + if (hdr == NULL) { + printf("error allocating blksz(%lu) buffer\n", blksz); + goto fail; + } + if (blkdev->block_read(blkdev->dev, ptn->start, + 1, (void *) hdr) != 1) { + printf("booti: failed to read bootimg header\n"); + goto fail; + } + if (memcmp(hdr->magic, FASTBOOT_BOOT_MAGIC, + FASTBOOT_BOOT_MAGIC_SIZE)) { + printf("booti: bad boot image magic\n"); + goto fail; + } + + bootimg_print_image_hdr(hdr); + + sector = ptn->start + (hdr->page_size / blksz); + blocks = DIV_ROUND_UP(hdr->kernel_size, blksz); + if (blkdev->block_read(blkdev->dev, sector, blocks, + (void *) hdr->kernel_addr) != + blocks) { + printf("booti: failed to read kernel\n"); + goto fail; + } + + sector += ALIGN(hdr->kernel_size, hdr->page_size) / blksz; + blocks = DIV_ROUND_UP(hdr->ramdisk_size, blksz); + if (blkdev->block_read(blkdev->dev, sector, blocks, + (void *) hdr->ramdisk_addr) != + blocks) { + printf("booti: failed to read ramdisk\n"); + goto fail; + } + if (need_post_ran) { + need_post_ran = 0; + if (partition_read_post(ptn)) { + printf("post-read commands for partition '%s' " + "failed\n", ptn->name); + goto fail; + } + } + } else { + unsigned addr; + void *kaddr, *raddr; + char *ep; + + addr = simple_strtoul(boot_source, &ep, 16); + if (ep == boot_source || *ep != '\0') { + printf("'%s' does not seem to be a partition nor " + "an address\n", boot_source); + /* this is most likely due to having no + * partition table in factory case, or could + * be argument is wrong. in either case, start + * fastboot mode. + */ + goto fail; + } + + hdr = malloc(sizeof(*hdr)); + if (hdr == NULL) { + printf("error allocating buffer\n"); + goto fail; + } + + /* set this aside somewhere safe */ + memcpy(hdr, (void *) addr, sizeof(*hdr)); + + if (memcmp(hdr->magic, FASTBOOT_BOOT_MAGIC, + FASTBOOT_BOOT_MAGIC_SIZE)) { + printf("booti: bad boot image magic\n"); + goto fail; + } + + bootimg_print_image_hdr(hdr); + + kaddr = (void *)(addr + hdr->page_size); + raddr = (void *)(kaddr + ALIGN(hdr->kernel_size, + hdr->page_size)); + memmove((void *)hdr->kernel_addr, kaddr, hdr->kernel_size); + memmove((void *)hdr->ramdisk_addr, raddr, hdr->ramdisk_size); + } + + printf("kernel @ %08x (%d)\n", hdr->kernel_addr, hdr->kernel_size); + printf("ramdisk @ %08x (%d)\n", hdr->ramdisk_addr, hdr->ramdisk_size); + +#ifdef CONFIG_CMDLINE_TAG + +#ifdef CONFIG_FASTBOOT_PRESERVE_BOOTARGS + setenv("hdr_cmdline", (char *)hdr->cmdline); +#else + { + /* static just to be safe when it comes to the stack */ + static char command_line[1024]; + int i, amt; + + /* Use the command line in the bootimg header instead of + * any hardcoded into u-boot. Also, Android wants the + * serial number on the command line instead of via + * tags so append the serial number to the bootimg header + * value and set the bootargs environment variable. + * do_bootm_linux() will use the bootargs environment variable + * to pass it to the kernel. Add the bootloader + * version too. + */ + amt = snprintf(command_line, + sizeof(command_line), + "%s androidboot.bootloader=%s", + hdr->cmdline, + CONFIG_FASTBOOT_VERSION_BOOTLOADER); + + for (i = 0; i < priv.num_device_info; i++) { + /* Append device specific information like + * MAC addresses and serialno + */ + amt += snprintf(command_line + amt, + sizeof(command_line) - amt, + " %s=%s", + priv.dev_info[i].name, + priv.dev_info[i].value); + } + + /* append serial number if it wasn't in device_info already */ + if (!strstr(command_line, FASTBOOT_SERIALNO_BOOTARG)) { + snprintf(command_line + amt, sizeof(command_line) - amt, + " %s=%s", FASTBOOT_SERIALNO_BOOTARG, + priv.serial_no); + } + + command_line[sizeof(command_line) - 1] = 0; + board_fbt_finalize_bootargs(command_line, sizeof(command_line)); + + setenv("bootargs", command_line); + } +#endif /* CONFIG_FASTBOOT_PRESERVE_BOOTARGS */ +#endif /* CONFIG_CMDLINE_TAG */ + + memset(&images, 0, sizeof(images)); + images.ep = hdr->kernel_addr; + images.rd_start = hdr->ramdisk_addr; + images.rd_end = hdr->ramdisk_addr + hdr->ramdisk_size; + free(hdr); + do_bootm_linux(0, 0, NULL, &images); + + puts("booti: Control returned to monitor - resetting...\n"); + do_reset(cmdtp, flag, argc, argv); + return 1; + +fail: + if (need_post_ran && partition_read_post(ptn)) + printf("post-read commands for partition '%s' failed\n", + ptn->name); + /* if booti fails, always start fastboot */ + free(hdr); /* hdr may be NULL, but that's ok. */ + return do_fastboot(NULL, 0, 0, NULL); +} + +U_BOOT_CMD( + booti, 2, 1, do_booti, + "boot android bootimg", + "[ <addr> | <partition> ]\n - boot application image\n" + "\t'addr' should be the address of the boot image which is\n" + "\tzImage+ramdisk.img if in memory. 'partition' is the name\n" + "\tof the partition to boot from. The default is to boot\n" + "\tfrom the 'boot' partition.\n" +); + +static void fbt_request_start_fastboot(void) +{ + char buf[512]; + char *old_preboot = getenv("preboot"); + printf("old preboot env = %s\n", old_preboot); + + fbt_clear_recovery_flag(); + + if (old_preboot) { + snprintf(buf, sizeof(buf), + "setenv preboot %s; fastboot", old_preboot); + setenv("preboot", buf); + } else + setenv("preboot", "setenv preboot; fastboot"); + + printf("%s: setting preboot env to %s\n", __func__, getenv("preboot")); +} + +/* + * Determine if we should enter fastboot mode based on board specific + * key press or parameter left in memory from previous boot. + * + * This is also where we initialize fbt private data. Even if we + * don't enter fastboot mode, we need our environment setup for + * things like unlock state, clearing reboot to recovery flag, etc. + */ +void fbt_preboot(void) +{ + enum fbt_reboot_type frt; + + /* need to init this ASAP so we know the unlocked state */ + fbt_fastboot_init(); + + if (board_fbt_key_pressed()) { + fbt_request_start_fastboot(); + return; + } + + frt = board_fbt_get_reboot_type(); + if (frt == FASTBOOT_REBOOT_RECOVERY) { + printf("\n%s: starting recovery img because of reboot flag\n", + __func__); + + return fbt_run_recovery(1); + } else if (frt == FASTBOOT_REBOOT_RECOVERY_WIPE_DATA) { + printf("\n%s: starting recovery img to wipe data " + "because of reboot flag\n", + __func__); + /* we've not initialized most of our state so don't + * save env in this case + */ + return fbt_run_recovery_wipe_data(); + } else if (frt == FASTBOOT_REBOOT_BOOTLOADER) { + + /* Case: %fastboot reboot-bootloader + * Case: %adb reboot bootloader + * Case: %adb reboot-bootloader + */ + printf("\n%s: starting fastboot because of reboot flag\n", + __func__); + fbt_request_start_fastboot(); + } else if (frt == FASTBOOT_REBOOT_NORMAL) { + /* explicit request for a regular reboot */ + printf("\n%s: request for a normal boot\n", + __func__); + fbt_clear_recovery_flag(); + } else { + /* unknown reboot cause (typically because of a cold boot). + * check if we had flag set to boot recovery and it + * was never cleared properly (i.e. recovery didn't finish). + * if so, jump to recovery again. + */ + char *run_recovery = getenv(FASTBOOT_RUN_RECOVERY_ENV_NAME); + if (run_recovery) { + printf("\n%s: starting recovery because of " + "saved reboot flag\n", __func__); + return fbt_run_recovery(0); + } + printf("\n%s: no special reboot flags, doing normal boot\n", + __func__); + } +} + +int fbt_send_info(const char *info) +{ + int len; + unsigned long space_in_log = CONFIG_FASTBOOT_LOG_SIZE - log_position; + unsigned long bytes_to_log; + + len = strlen(info); + + /* check if relocation is done before we can use globals */ + if (gd->flags & GD_FLG_RELOC) { + if (len > space_in_log) + bytes_to_log = space_in_log; + else + bytes_to_log = len; + + if (bytes_to_log) { + strncpy(&log_buffer[log_position], info, bytes_to_log); + log_position += bytes_to_log; + } + } + + return fbt_send_raw_info(info, len); +} diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 04ab0f19b..4d0d75b59 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -462,7 +462,7 @@ static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) } } -static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int i, ret = 0; ulong addr; diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 5bcc32467..b09dea6dc 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -339,7 +339,7 @@ ulong getenv_hex(const char *varname, ulong default_val) } #ifndef CONFIG_SPL_BUILD -static int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { if (argc < 2) return CMD_RET_USAGE; @@ -684,7 +684,7 @@ ulong getenv_ulong(const char *name, int base, ulong default_val) #ifndef CONFIG_SPL_BUILD #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE) -static int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, +int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { printf("Saving Environment to %s...\n", env_name_spec); |