blob: 0cef2ec06b9616c82d5d3299d90899b3911b8169 [file] [log] [blame]
/*
* 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 <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;
}
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);
}