diff options
Diffstat (limited to 'drivers/usb/gadget/u_fastboot_mmc.c')
| -rw-r--r-- | drivers/usb/gadget/u_fastboot_mmc.c | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/drivers/usb/gadget/u_fastboot_mmc.c b/drivers/usb/gadget/u_fastboot_mmc.c new file mode 100644 index 000000000..2ed64efcc --- /dev/null +++ b/drivers/usb/gadget/u_fastboot_mmc.c @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2013 Texas Instruments + * + * Author : Pankaj Bharadiya <pankaj.bharadiya@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * 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 Sitara 2011 u-boot by + * Mohammed Afzal M A <afzal@ti.com> + * + * 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 <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include "g_fastboot.h" +#include <environment.h> +#include <mmc.h> +#include <sparse_format.h> + +#define EFI_VERSION 0x00010000 +#define EFI_ENTRIES 128 +#define EFI_NAMELEN 36 + +struct partition { + const char *name; + unsigned size_kb; +}; + +/* eMMC partition layout (All sizes are in kB) + * Modify the below partition table to change the GPT configuration. + * The entry for each partition can be modified as per the requirement. + */ +static struct partition partitions[] = { + { "-", 128 }, /* Master Boot Record and GUID Partition Table */ + { "spl", 128 }, /* First stage bootloader */ + { "bootloader", 512 }, /* Second stage bootloader */ + { "misc", 128 }, /* Rserved for internal purpose */ + { "-", 128 }, /* Reserved */ + { "recovery", 8*1024 }, /* Recovery partition */ + { "boot", 8*1024 }, /* Partition contains kernel + ramdisk images */ + { "system", 512*1024 }, /* Android file system */ + { "cache", 256*1024 }, /* Store Application Cache */ + { "userdata", 1047*1024 }, /* User data */ + { "media", 0 }, /* Media files */ + { 0, 0 }, +}; + + +static const u8 partition_type[16] = { + 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, + 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, +}; + +static const u8 random_uuid[16] = { + 0xff, 0x1f, 0xf2, 0xf9, 0xd4, 0xa8, 0x0e, 0x5f, + 0x97, 0x46, 0x59, 0x48, 0x69, 0xae, 0xc3, 0x4e, +}; + +struct efi_entry { + u8 type_uuid[16]; + u8 uniq_uuid[16]; + u64 first_lba; + u64 last_lba; + u64 attr; + u16 name[EFI_NAMELEN]; +}; + +struct efi_header { + u8 magic[8]; + + u32 version; + u32 header_sz; + + u32 crc32; + u32 reserved; + + u64 header_lba; + u64 backup_lba; + u64 first_lba; + u64 last_lba; + + u8 volume_uuid[16]; + + u64 entries_lba; + + u32 entries_count; + u32 entries_size; + u32 entries_crc32; +} __attribute__((packed)); + +struct ptable { + u8 mbr[512]; + union { + struct efi_header header; + u8 block[512]; + }; + struct efi_entry entry[EFI_ENTRIES]; +}; + +static void init_mbr(u8 *mbr, u32 blocks) +{ + mbr[0x1be] = 0x00; /* nonbootable */ + mbr[0x1bf] = 0xFF; /* bogus CHS */ + mbr[0x1c0] = 0xFF; + mbr[0x1c1] = 0xFF; + + mbr[0x1c2] = 0xEE; /* GPT partition */ + mbr[0x1c3] = 0xFF; /* bogus CHS */ + mbr[0x1c4] = 0xFF; + mbr[0x1c5] = 0xFF; + + mbr[0x1c6] = 0x01; /* start */ + mbr[0x1c7] = 0x00; + mbr[0x1c8] = 0x00; + mbr[0x1c9] = 0x00; + + memcpy(mbr + 0x1ca, &blocks, sizeof(u32)); + + mbr[0x1fe] = 0x55; + mbr[0x1ff] = 0xaa; +} + +static void start_ptbl(struct ptable *ptbl, unsigned blocks) +{ + struct efi_header *hdr = &ptbl->header; + + memset(ptbl, 0, sizeof(*ptbl)); + + init_mbr(ptbl->mbr, blocks - 1); + + memcpy(hdr->magic, "EFI PART", 8); + hdr->version = EFI_VERSION; + hdr->header_sz = sizeof(struct efi_header); + hdr->header_lba = 1; + hdr->backup_lba = blocks - 1; + hdr->first_lba = 34; + hdr->last_lba = blocks - 1; + memcpy(hdr->volume_uuid, random_uuid, 16); + hdr->entries_lba = 2; + hdr->entries_count = EFI_ENTRIES; + hdr->entries_size = sizeof(struct efi_entry); +} + +static void end_ptbl(struct ptable *ptbl) +{ + struct efi_header *hdr = &ptbl->header; + u32 n; + + n = crc32(0, 0, 0); + n = crc32(n, (void *) ptbl->entry, sizeof(ptbl->entry)); + hdr->entries_crc32 = n; + + n = crc32(0, 0, 0); + n = crc32(0, (void *) &ptbl->header, sizeof(ptbl->header)); + hdr->crc32 = n; +} + +int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name) +{ + struct efi_header *hdr = &ptbl->header; + struct efi_entry *entry = ptbl->entry; + unsigned n; + + if (first < 34) { + printf("partition '%s' overlaps partition table\n", name); + return -1; + } + + if (last > hdr->last_lba) { + printf("partition '%s' does not fit\n", name); + return -1; + } + for (n = 0; n < EFI_ENTRIES; n++, entry++) { + if (entry->last_lba) + continue; + memcpy(entry->type_uuid, partition_type, 16); + memcpy(entry->uniq_uuid, random_uuid, 16); + entry->uniq_uuid[0] = n; + entry->first_lba = first; + entry->last_lba = last; + for (n = 0; (n < EFI_NAMELEN) && *name; n++) + entry->name[n] = *name++; + return 0; + } + printf("out of partition table entries\n"); + return -1; +} + +void import_efi_partition(struct efi_entry *entry) +{ + struct fastboot_ptentry e; + int n; + if (memcmp(entry->type_uuid, partition_type, sizeof(partition_type))) + return; + for (n = 0; n < (sizeof(e.name)-1); n++) + e.name[n] = entry->name[n]; + e.name[n] = 0; + e.start = entry->first_lba; + e.length = (entry->last_lba - entry->first_lba + 1) * 512; + e.flags = 0; + + if (!strcmp(e.name, "environment")) + e.flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_ENV; + fastboot_flash_add_ptn(&e); + + if (e.length > 0x100000) + printf("%8d %7dM %s\n", e.start, e.length/0x100000, e.name); + else + printf("%8d %7dK %s\n", e.start, e.length/0x400, e.name); +} + +static int load_ptbl(void) +{ + static unsigned char data[512]; + static struct efi_entry entry[4]; + int n, m; + char source[32], dest[32], length[32]; + + char *mmc_read[5] = {"mmc", "read", NULL, NULL, NULL}; + + /* read mbr */ + mmc_read[2] = source; + mmc_read[3] = dest; + mmc_read[4] = length; + + sprintf(source, "0x%x", data); + sprintf(dest, "0x%x", 0x1); + sprintf(length, "0x%x", 1); + + if (do_mmcops(NULL, 0, 5, mmc_read)) { + printf("Reading boot magic FAILED!\n"); + return -1; + } + + if (memcmp(data, "EFI PART", 8)) { + printf("efi partition table not found\n"); + return -1; + } + for (n = 0; n < (128/4); n++) { + + /* read partition */ + source[0] = '\0'; + dest[0] = '\0'; + length[0] = '\0'; + mmc_read[2] = source; + mmc_read[3] = dest; + mmc_read[4] = length; + + sprintf(source, "0x%x", entry); + sprintf(dest, "0x%x", 0x1+n); + sprintf(length, "0x%x", 1); + + if (do_mmcops(NULL, 0, 5, mmc_read)) { + printf("Reading boot magic FAILED!\n"); + return -1; + } + for (m = 0; m < 4; m++) + import_efi_partition(entry + m); + } + return 0; +} + + +static struct ptable the_ptable; + +int do_format(void) +{ + struct ptable *ptbl = &the_ptable; + unsigned sector_sz, blocks; + unsigned next; + int n; + + printf("\ndo_format ..!!"); + /* get mmc info */ + struct mmc *mmc = find_mmc_device(CONFIG_MMC_FASTBOOT_DEV); + if (mmc == 0) { + printf("no mmc device at slot %d", CONFIG_MMC_FASTBOOT_DEV); + return -1; + } + + mmc->has_init = 0; + if (mmc_init(mmc)) { + + printf("\n mmc init FAILED"); + return -1; + } else{ + printf("\nmmc capacity is:0x%x", mmc->capacity); + printf("\nmmc: number of blocks:0x%x", mmc->block_dev.lba); + printf("\nmmc: block size:0x%x", mmc->block_dev.blksz); + } + + blocks = mmc->block_dev.lba; + sector_sz = mmc->block_dev.blksz; + + start_ptbl(ptbl, blocks); + n = 0; + next = 0; + for (n = 0, next = 0; partitions[n].name; n++) { + /* 10/11 : below line change size from KB to no of blocks */ + unsigned sz = partitions[n].size_kb*2 ; + if (!strcmp(partitions[n].name, "-")) { + next += sz; + continue; + } + if (sz == 0) + sz = blocks - next; + if (add_ptn(ptbl, next, next + sz - 1, partitions[n].name)) + return -1; + next += sz; + } + end_ptbl(ptbl); + + fastboot_flash_reset_ptn(); + + /* 10/11:modified as per PSP release support */ + char *mmc_write[5] = {"mmc", "write", NULL, NULL, NULL}; + char source[32], dest[32], length[32]; + + char dev[2]; + char *mmc_dev[3] = {"mmc", "dev", NULL}; + + mmc_dev[2] = dev; + sprintf(dev,"0x%x", CONFIG_MMC_FASTBOOT_DEV); + + if (do_mmcops(NULL, 0, 3, mmc_dev)) { + printf("MMC DEV: %d selection FAILED!\n", CONFIG_MMC_FASTBOOT_DEV); + return -1; + } + + mmc_write[2] = source; + mmc_write[3] = dest; + mmc_write[4] = length; + + sprintf(source, "0x%x", (void *)ptbl); + sprintf(dest, "0x%x", 0x00); + sprintf(length, "0x%x", (sizeof(struct ptable)/512)+1); + + if (do_mmcops(NULL, 0, 5, mmc_write)) { + printf("Writing mbr is FAILED!\n"); + return -1; + } else { + printf("Writing mbr is DONE!\n"); + } + + printf("\nnew partition table:\n"); + load_ptbl(); + + return 0; +} + +struct fastboot_config fastboot_cfg; + +extern env_t *env_ptr; +//extern int do_env_save (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); + +static int write_mmc_chunk(unsigned int source, unsigned int dest_sector, unsigned int num_sectors) +{ + char s_source[32], s_dest[32], s_length[32]; + char *mmc_write[5] = {"mmc", "write", NULL, NULL, NULL}; + char dev[12]; + char *mmc_dev[3] = {"mmc", "dev", NULL}; + + sprintf(s_source, "0x%x", source); + sprintf(s_dest, "0x%x", dest_sector); + sprintf(s_length, "0x%x", num_sectors); + mmc_write[2] = s_source; + mmc_write[3] = s_dest; + mmc_write[4] = s_length; + sprintf(dev,"0x%x", CONFIG_MMC_FASTBOOT_DEV); + mmc_dev[2] = dev; + +// printf("%s %s %s\n", s_source, s_dest, s_length); + if (do_mmcops(NULL, 0, 5, mmc_write)) { + return -1; + } else { + return 0; + } +} + +static int flash_mmc_sparse_img(unsigned int ptn_start_sector) +{ + void *data; + unsigned int chunk; + unsigned int chunk_data_sz; + sparse_header_t *sparse_header; + chunk_header_t *chunk_header; + uint32_t total_blocks = 0; + uint32_t sectors_per_block; + + data = fastboot_cfg.transfer_buffer; + sparse_header = (sparse_header_t *)data; +#ifdef DEBUG_SPARSE + printf("=== Sparse Image Header ===\n"); + printf("magic: 0x%x\n", sparse_header->magic); + printf("major_version: 0x%x\n", sparse_header->major_version); + printf("minor_version: 0x%x\n", sparse_header->minor_version); + printf("file_hdr_sz: %u\n", sparse_header->file_hdr_sz); + printf("chunk_hdr_sz: %u\n", sparse_header->chunk_hdr_sz); + printf("blk_sz: %u\n", sparse_header->blk_sz); + printf("total_blks: %u\n", sparse_header->total_blks); + printf("total_chunks: %u\n", sparse_header->total_chunks); +#endif + data += sparse_header->file_hdr_sz; + + sectors_per_block = sparse_header->blk_sz/512; + + for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) { + /* Read and skip over chunk header */ + chunk_header = (chunk_header_t *)data; + data += sizeof(chunk_header_t); +#ifdef DEBUG_SPARSE + printf("=== Chunk Header ===\n"); + printf("chunk_type: 0x%x\n", chunk_header->chunk_type); + printf("chunk_data_sz: 0x%x\n", chunk_header->chunk_sz); + printf("total_size: 0x%x\n", chunk_header->total_sz); +#endif + chunk_data_sz = sparse_header->blk_sz * chunk_header->chunk_sz; + switch (chunk_header->chunk_type) { + case CHUNK_TYPE_RAW: + if (write_mmc_chunk((unsigned int)data, + ptn_start_sector + (total_blocks * sectors_per_block), + chunk_header->chunk_sz * sectors_per_block) != 0) { + return -1; + } + data += chunk_data_sz; + break; + case CHUNK_TYPE_DONT_CARE: + break; + case CHUNK_TYPE_CRC32: + break; + default: + printf("Unknown chunk type\n"); + return -1; + } + total_blocks += chunk_header->chunk_sz; + } + return 0; +} + +int handle_flash(char *part_name, char *response) +{ + int status = 0; + + if (fastboot_cfg.download_bytes) { + struct fastboot_ptentry *ptn; + + /* Next is the partition name */ + ptn = fastboot_flash_find_ptn(part_name); + + if (ptn == 0) { + printf("Partition:[%s] does not exist\n", part_name); + sprintf(response, "FAILpartition does not exist"); + } else if ((fastboot_cfg.download_bytes > ptn->length) && + !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) { + printf("Image too large for the partition\n"); + sprintf(response, "FAILimage too large for partition"); + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) { + /* Check if this is not really a flash write, + * but instead a saveenv + */ + unsigned int i = 0; + /* Env file is expected with a NULL delimeter between + * env variables So replace New line Feeds (0x0a) with + * NULL (0x00) + */ + for (i = 0; i < fastboot_cfg.download_bytes; i++) { + if (fastboot_cfg.transfer_buffer[i] == 0x0a) + fastboot_cfg.transfer_buffer[i] = 0x00; + } + memset(env_ptr->data, 0, ENV_SIZE); + memcpy(env_ptr->data, fastboot_cfg.transfer_buffer, fastboot_cfg.download_bytes); + //do_env_save(NULL, 0, 1, NULL); + printf("saveenv to '%s' DONE!\n", ptn->name); + sprintf(response, "OKAY"); + } else { + /* Normal case */ + sparse_header_t *sparse_header; + char source[32], dest[32], length[32]; + source[0] = '\0'; + dest[0] = '\0'; + length[0] = '\0'; + + char *mmc_write[5] = {"mmc", "write", NULL, NULL, NULL}; + char *mmc_init[2] = {"mmc", "rescan",}; + char dev[12]; + char *mmc_dev[3] = {"mmc", "dev", NULL}; + + mmc_dev[2] = dev; + sprintf(dev,"0x%x", CONFIG_MMC_FASTBOOT_DEV); + + if (do_mmcops(NULL, 0, 3, mmc_dev)) { + printf("MMC DEV: %d selection FAILED!\n", CONFIG_MMC_FASTBOOT_DEV); + return -1; + } + + printf("Initializing '%s'\n", ptn->name); + if (do_mmcops(NULL, 0, 2, mmc_init)) + sprintf(response, "FAIL:Init of MMC card"); + else + sprintf(response, "OKAY"); + + sparse_header = (sparse_header_t *)fastboot_cfg.transfer_buffer; + if (sparse_header->magic == SPARSE_HEADER_MAGIC) { + printf("Image is sparse format\n"); + if (flash_mmc_sparse_img(ptn->start) == 0) { + sprintf(response, "OKAY"); + printf("Writing '%s' DONE!\n", ptn->name); + } else { + sprintf(response, "FAIL: Write partition"); + printf("Writing '%s' FAILED!\n", ptn->name); + } + return 0; + } + + mmc_write[2] = source; + mmc_write[3] = dest; + mmc_write[4] = length; + + sprintf(source, "0x%x", fastboot_cfg.transfer_buffer); + sprintf(dest, "0x%x", ptn->start); + sprintf(length, "0x%x", (fastboot_cfg.download_bytes/512)+1); + + printf("Writing '%s'\n", ptn->name); + if (do_mmcops(NULL, 0, 5, mmc_write)) { + printf("Writing '%s' FAILED!\n", ptn->name); + sprintf(response, "FAIL: Write partition"); + } else { + printf("Writing '%s' DONE!\n", ptn->name); + sprintf(response, "OKAY"); + } + } + } else { + sprintf(response, "FAILno image downloaded"); + } + return 0; +} + +int board_mmc_fbtptn_init(void) +{ + char *mmc_init[2] = {"mmc", "rescan",}; + char dev[2]; + char *mmc_dev[3] = {"mmc", "dev", NULL}; + + mmc_dev[2] = dev; + sprintf(dev,"0x%x", CONFIG_MMC_FASTBOOT_DEV); + + if (do_mmcops(NULL, 0, 3, mmc_dev)) { + printf("MMC DEV: %d selection FAILED!\n", CONFIG_MMC_FASTBOOT_DEV); + return -1; + } + + if (do_mmcops(NULL, 0, 2, mmc_init)) { + printf("FAIL:Init of MMC card\n"); + return 1; + } + + printf("Loading efi partition table:\n"); + return load_ptbl(); +} + |