/*
* Copyright (c) 2014-2015, Linaro Ltd and Contributors. All rights reserved.
* Copyright (c) 2014-2015, Hisilicon Ltd and Contributors. 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.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* 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 HOLDER 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 <debug.h>
#include <dw_mmc.h>
#include <errno.h>
#include <io_storage.h>
#include <mmio.h>
#include <partitions.h>
#include <platform_def.h>
#include <string.h>
#include "hikey_private.h"
#define EFI_ENTRIES 128
#define EFI_ENTRY_SIZE (sizeof(struct efi_entry))
#define EFI_MBR_SIZE 512
#define EFI_HEADER_SIZE 512
#define EFI_TOTAL_SIZE (EFI_MBR_SIZE + EFI_HEADER_SIZE + \
EFI_ENTRY_SIZE * EFI_ENTRIES)
struct efi_header {
char signature[8];
uint32_t revision;
uint32_t size;
uint32_t header_crc;
uint32_t reserved;
uint64_t current_lba;
uint64_t backup_lba;
uint64_t first_lba;
uint64_t last_lba;
uint8_t disk_uuid[16];
/* starting LBA of array of partition entries */
uint64_t part_lba;
/* number of partition entries in array */
uint32_t part_num;
/* size of a single partition entry (usually 128) */
uint32_t part_size;
uint32_t part_crc;
};
struct efi_entry {
uint8_t type_uuid[16];
uint8_t uniq_uuid[16];
uint64_t first_lba;
uint64_t last_lba;
uint64_t attr;
uint16_t name[EFI_NAMELEN];
};
/* the first entry is dummy for ptable (covers both primary & secondary) */
static struct ptentry ptable[EFI_ENTRIES + 1];
static int entries; /* partition entry entries */
static void dump_entries(void)
{
int i;
VERBOSE("Partition table with %d entries:\n", entries);
for (i = 0; i < entries; i++) {
VERBOSE("%s %llx-%llx\n", ptable[i].name,
ptable[i].start,
ptable[i].start + ptable[i].length - 4);
}
}
static int convert_ascii_string(uint16_t *str_in, uint8_t *str_out)
{
uint8_t *name = (uint8_t *)str_in;
int i;
if (name[0] == '\0' || !str_in || !str_out)
return -EINVAL;
for (i = 1; i < (EFI_NAMELEN << 1); i += 2) {
if (name[i] != '\0')
return -EINVAL;
}
for (i = 0; i < (EFI_NAMELEN << 1); i += 2) {
str_out[i >> 1] = name[i];
if (name[i] == '\0')
break;
}
return 0;
}
static int parse_entry(uintptr_t buf)
{
struct efi_entry *entry = (struct efi_entry *)buf;
int ret;
/* exhaused partition entry */
if ((entry->first_lba == 0) && (entry->last_lba == 0))
return 1;
ret = convert_ascii_string(entry->name, (uint8_t *)ptable[entries].name);
if (ret < 0)
return ret;
ptable[entries].start = (uint64_t)entry->first_lba * 512;
ptable[entries].length = (uint64_t)(entry->last_lba - entry->first_lba + 1) * 512;
entries++;
return 0;
}
/* create dummy entry for ptable */
static void create_dummy_entry(void)
{
int bytes;
ptable[entries].start = 0;
ptable[entries].length = 0;
bytes = sprintf(ptable[entries].name, "ptable");
ptable[entries].name[bytes] = '\0';
entries++;
}
struct ptentry *find_ptn(const char *str)
{
struct ptentry *ptn = NULL;
int i;
for (i = 0; i < entries; i++) {
if (!strcmp(ptable[i].name, str)) {
ptn = &ptable[i];
break;
}
}
return ptn;
}
int get_partition(void)
{
int result = IO_FAIL;
int i, ret, num_entries;
size_t bytes_read;
uintptr_t emmc_dev_handle, spec, img_handle;
unsigned int buf[MMC_BLOCK_SIZE >> 2];
struct efi_header *hd = NULL;
create_dummy_entry();
result = plat_get_image_source(NORMAL_EMMC_NAME, &emmc_dev_handle,
&spec);
if (result) {
WARN("failed to open eMMC normal partition\n");
return result;
}
result = io_open(emmc_dev_handle, spec, &img_handle);
if (result != IO_SUCCESS) {
WARN("Failed to open eMMC device\n");
return result;
}
result = io_seek(img_handle, IO_SEEK_SET, 0);
if (result)
goto exit;
result = io_read(img_handle, (uintptr_t)buf, EFI_MBR_SIZE,
&bytes_read);
if ((result != IO_SUCCESS) || (bytes_read < EFI_MBR_SIZE)) {
WARN("Failed to read eMMC (%i)\n", result);
goto exit;
}
/* check the magic number in last word */
if (buf[(MMC_BLOCK_SIZE >> 2) - 1] != 0xaa550000) {
WARN("Can't find MBR protection information\n");
goto exit;
}
result = io_read(img_handle, (uintptr_t)buf, EFI_HEADER_SIZE,
&bytes_read);
if ((result != IO_SUCCESS) || (bytes_read < EFI_HEADER_SIZE)) {
WARN("Failed to read eMMC (%i)\n", result);
goto exit;
}
hd = (struct efi_header *)((uintptr_t)buf);
if (strncmp(hd->signature, "EFI PART", 8)) {
WARN("Failed to find partition table\n");
goto exit;
}
num_entries = hd->part_num;
for (i = 0; i < num_entries; i++) {
result = io_read(img_handle, (uintptr_t)buf, EFI_HEADER_SIZE,
&bytes_read);
if ((result != IO_SUCCESS) || (bytes_read < EFI_HEADER_SIZE)) {
WARN("Failed to read eMMC (%i)\n", result);
goto exit;
}
/* each header contains four partition entries */
ret = parse_entry((uintptr_t)buf);
if (ret)
break;
ret = parse_entry((uintptr_t)buf + EFI_ENTRY_SIZE);
if (ret)
break;
ret = parse_entry((uintptr_t)buf + EFI_ENTRY_SIZE * 2);
if (ret)
break;
ret = parse_entry((uintptr_t)buf + EFI_ENTRY_SIZE * 3);
if (ret)
break;
}
exit:
io_close(img_handle);
update_fip_spec();
dump_entries();
return result;
}