blob: fd99aae3361ff62d853185ab62568182bbc1d8c6 [file] [log] [blame]
/*
* 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 <fastboot.h>
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_FASTBOOT_VERSION_BOOTLOADER
#define CONFIG_FASTBOOT_VERSION_BOOTLOADER FASTBOOT_VERSION
#endif
#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 unsigned long 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);
/* 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");
/*
* 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", ptn->name);
sprintf(priv.response, "FAILpartition does not exist");
return;
}
#ifndef CONFIG_MFG
/* don't allow erasing a valid device info partition in a production
* u-boot */
if (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, outlen = 0;
unsigned long blksz = priv.dev_desc->blksz;
u64 section_size = (u64)num_blks * blksz;
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 = 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("\nsparse: out-length-0x%d 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)
{
disk_partition_t *ptn;
if (!priv.unlocked) {
sprintf(priv.response, "FAILdevice is locked");
return;
}
if (!priv.d_bytes) {
sprintf(priv.response, "FAILno image downloaded");
return;
}
ptn = fastboot_flash_find_ptn(cmdbuf + 6);
if (ptn == 0) {
sprintf(priv.response, "FAILpartition does not exist");
return;
}
/* Prevent using flash command to write to device_info partition */
if (is_info_partition(ptn)) {
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.transfer_buffer,
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 (do_env_save(NULL, 0, 0, NULL)) {
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.transfer_buffer)->magic
== SPARSE_HEADER_MAGIC) {
printf("fastboot: %s is in sparse format\n", ptn->name);
if (!do_unsparse(ptn, priv.transfer_buffer,
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.transfer_buffer);
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 */
}
static void fbt_handle_getvar(char *cmdbuf)
{
strcpy(priv.response, "OKAY");
char *subcmd = cmdbuf + strlen("getvar:");
char *value = NULL;
if (!strcmp(subcmd, "version"))
value = version_string;
else if (!strcmp(subcmd, "version-baseband"))
value = "n/a";
else if (!strcmp(subcmd, "version-bootloader"))
value = CONFIG_FASTBOOT_VERSION_BOOTLOADER;
else if (!strcmp(subcmd, "unlocked"))
value = (priv.unlocked ? "yes" : "no");
else if (!strcmp(subcmd, "secure")) {
/* we use the inverse meaning of unlocked */
value = (priv.unlocked ? "no" : "yes");
}
#if defined (CONFIG_OMAP)
else if (!strcmp(subcmd, "device_type")) {
switch(get_device_type()) {
case TST_DEVICE:
value = "TST";
break;
case EMU_DEVICE:
value = "EMU";
break;
case HS_DEVICE:
value = "HS";
break;
case GP_DEVICE:
value = "GP";
break;
default:
value = "unknown";
break;
}
}
#endif
else if (!strcmp(subcmd, "product"))
value = FASTBOOT_PRODUCT_NAME;
else if (!strcmp(subcmd, "serialno"))
value = priv.serial_no;
#ifdef CONFIG_FASTBOOT_UBOOT_GETVAR
else {
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.
*/
strncpy(priv.response + 4, value, (sizeof(priv.response) - 4));
}
}
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);
}
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(void)
{
/* 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.
*/
int bytes_left = log_position;
char *line_start = log_buffer;
while (bytes_left) {
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;
bytes_left -= len;
} else {
fbt_send_raw_info(line_start, strlen(line_start));
break;
}
}
}
static void fbt_handle_oem(char *cmdbuf)
{
cmdbuf += 4;
/* %fastboot oem recovery */
if (strcmp(cmdbuf, "recovery") == 0) {
FBTDBG("oem recovery\n");
fbt_handle_reboot("reboot-recovery");
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 your device will invalidate\n");
printf("\tyour warranty and wipe user data.\n");
printf("\tDo 'fastboot oem unlock_accept' to accept\n");
printf("\tthese conditions within %d seconds.\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");
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 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 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;
}
/* %fastboot oem log */
if (strcmp(cmdbuf, "log") == 0) {
FBTDBG("oem %s\n", cmdbuf);
fbt_dump_log();
strcpy(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.d_bytes) &&
(CONFIG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE < priv.d_bytes)) {
char start[32];
char *booti[3] = { "booti", NULL, NULL, };
char *go[3] = { "go", NULL, NULL, };
/*
* 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;
booti[1] = go[1] = start;
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, 2, booti);
printf("do_booti() returned, trying go..\n");
FBTINFO("Booting raw image..\n");
do_go(NULL, 0, 2, go);
FBTERR("booting failed, reset the board\n");
}
sprintf(priv.response, "FAILinvalid boot image");
}
static void fbt_rx_process_download(unsigned char *buffer, int length)
{
unsigned int xfr_size;
if (length == 0) {
FBTWARN("empty buffer download\n");
return;
}
xfr_size = priv.d_size - priv.d_bytes;
if (xfr_size > length)
xfr_size = length;
memcpy(priv.transfer_buffer + priv.d_bytes, buffer, xfr_size);
priv.d_bytes += xfr_size;
if (priv.d_bytes >= priv.d_size) {
priv.d_size = 0;
strcpy(priv.response, "OKAY");
priv.flag |= FASTBOOT_FLAG_RESPONSE;
FBTINFO("downloaded %llu bytes\n", priv.d_bytes);
}
}
/* XXX: Replace magic number & strings with macros */
static void fbt_rx_process(unsigned char *buffer, int length)
{
/* Generic failed response */
strcpy(priv.response, "FAIL");
if (!priv.d_size) {
/* command */
char *cmdbuf = (char *) buffer;
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);
}
/* %fastboot reboot
* %fastboot reboot-bootloader
*/
else if (memcmp(cmdbuf, "reboot", 6) == 0) {
FBTDBG("reboot or reboot-bootloader\n");
fbt_handle_reboot(cmdbuf);
return;
}
/* %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);
}
}
priv.flag |= FASTBOOT_FLAG_RESPONSE;
priv.executing_command = 0;
return;
}
fbt_rx_process_download(buffer, length);
}
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);
fbt_rx_process(ep->rcv_urb->buffer, ep->rcv_urb->actual_length);
/* Required to poison rx urb buffer as in omapzoom ?,
yes, as fastboot command are sent w/o NULL termination.
Attempt is made here to reduce poison length, may be safer
to posion the whole buffer, also it is assumed that at
the time of creation of urb it is poisoned */
memset(ep->rcv_urb->buffer, 0, ep->rcv_urb->actual_length);
ep->rcv_urb->actual_length = 0;
}
}
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;
}
}
/*
* 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 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;
}
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")));
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")));
/* 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;
}
printf("Starting fastboot protocol\n");
board_fbt_start();
fbt_fastboot_init();
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;
}
}
fbt_handle_response();
}
priv.exit |= ctrlc();
if (priv.exit) {
FBTINFO("fastboot end\n");
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);
return cmd_usage(cmdtp);
}
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);
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.
*/
void fbt_preboot(void)
{
enum fbt_reboot_type frt;
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;
}
if (board_fbt_key_pressed()) {
fbt_request_start_fastboot();
return;
}
frt = board_fbt_get_reboot_type();
if (frt == FASTBOOT_REBOOT_RECOVERY) {
char *const boot_cmd[] = {"booti", "recovery"};
printf("\n%s: starting recovery img because of reboot flag\n",
__func__);
do_booti(NULL, 0, ARRAY_SIZE(boot_cmd), boot_cmd);
/* returns if recovery.img is bad
* Default to normal boot
*/
printf("\nfastboot: Error: Invalid recovery img\n");
return;
} 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
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);
}