/* * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> #include <debug.h> #include <gpt.h> #include <io_storage.h> #include <mbr.h> #include <partition.h> #include <platform.h> #include <string.h> static uint8_t mbr_sector[PARTITION_BLOCK_SIZE]; partition_entry_list_t list; #if LOG_LEVEL >= LOG_LEVEL_VERBOSE static void dump_entries(int num) { char name[EFI_NAMELEN]; int i, j, len; VERBOSE("Partition table with %d entries:\n", num); for (i = 0; i < num; i++) { len = snprintf(name, EFI_NAMELEN, "%s", list.list[i].name); for (j = 0; j < EFI_NAMELEN - len - 1; j++) { name[len + j] = ' '; } name[EFI_NAMELEN - 1] = '\0'; VERBOSE("%d: %s %lx-%lx\n", i + 1, name, list.list[i].start, list.list[i].start + list.list[i].length - 4); } } #else #define dump_entries(num) ((void)num) #endif /* * Load the first sector that carries MBR header. * The MBR boot signature should be always valid whether it's MBR or GPT. */ static int load_mbr_header(uintptr_t image_handle, mbr_entry_t *mbr_entry) { size_t bytes_read; uintptr_t offset; int result; assert(mbr_entry != NULL); /* MBR partition table is in LBA0. */ result = io_seek(image_handle, IO_SEEK_SET, MBR_OFFSET); if (result != 0) { WARN("Failed to seek (%i)\n", result); return result; } result = io_read(image_handle, (uintptr_t)&mbr_sector, PARTITION_BLOCK_SIZE, &bytes_read); if (result != 0) { WARN("Failed to read data (%i)\n", result); return result; } /* Check MBR boot signature. */ if ((mbr_sector[PARTITION_BLOCK_SIZE - 2] != MBR_SIGNATURE_FIRST) || (mbr_sector[PARTITION_BLOCK_SIZE - 1] != MBR_SIGNATURE_SECOND)) { return -ENOENT; } offset = (uintptr_t)&mbr_sector + MBR_PRIMARY_ENTRY_OFFSET; memcpy(mbr_entry, (void *)offset, sizeof(mbr_entry_t)); return 0; } /* * Load GPT header and check the GPT signature. * If partiton numbers could be found, check & update it. */ static int load_gpt_header(uintptr_t image_handle) { gpt_header_t header; size_t bytes_read; int result; result = io_seek(image_handle, IO_SEEK_SET, GPT_HEADER_OFFSET); if (result != 0) { return result; } result = io_read(image_handle, (uintptr_t)&header, sizeof(gpt_header_t), &bytes_read); if ((result != 0) || (sizeof(gpt_header_t) != bytes_read)) { return result; } if (memcmp(header.signature, GPT_SIGNATURE, sizeof(header.signature)) != 0) { return -EINVAL; } /* partition numbers can't exceed PLAT_PARTITION_MAX_ENTRIES */ list.entry_count = header.list_num; if (list.entry_count > PLAT_PARTITION_MAX_ENTRIES) { list.entry_count = PLAT_PARTITION_MAX_ENTRIES; } return 0; } static int load_gpt_entry(uintptr_t image_handle, gpt_entry_t *entry) { size_t bytes_read; int result; assert(entry != NULL); result = io_read(image_handle, (uintptr_t)entry, sizeof(gpt_entry_t), &bytes_read); if (sizeof(gpt_entry_t) != bytes_read) return -EINVAL; return result; } static int verify_partition_gpt(uintptr_t image_handle) { gpt_entry_t entry; int result, i; for (i = 0; i < list.entry_count; i++) { result = load_gpt_entry(image_handle, &entry); assert(result == 0); result = parse_gpt_entry(&entry, &list.list[i]); if (result != 0) { break; } } if (i == 0) { return -EINVAL; } /* * Only records the valid partition number that is loaded from * partition table. */ list.entry_count = i; dump_entries(list.entry_count); return 0; } int load_partition_table(unsigned int image_id) { uintptr_t dev_handle, image_handle, image_spec = 0; mbr_entry_t mbr_entry; int result; result = plat_get_image_source(image_id, &dev_handle, &image_spec); if (result != 0) { WARN("Failed to obtain reference to image id=%u (%i)\n", image_id, result); return result; } result = io_open(dev_handle, image_spec, &image_handle); if (result != 0) { WARN("Failed to access image id=%u (%i)\n", image_id, result); return result; } result = load_mbr_header(image_handle, &mbr_entry); if (result != 0) { WARN("Failed to access image id=%u (%i)\n", image_id, result); return result; } if (mbr_entry.type == PARTITION_TYPE_GPT) { result = load_gpt_header(image_handle); assert(result == 0); result = io_seek(image_handle, IO_SEEK_SET, GPT_ENTRY_OFFSET); assert(result == 0); result = verify_partition_gpt(image_handle); } else { /* MBR type isn't supported yet. */ result = -EINVAL; goto exit; } exit: io_close(image_handle); return result; } const partition_entry_t *get_partition_entry(const char *name) { int i; for (i = 0; i < list.entry_count; i++) { if (strcmp(name, list.list[i].name) == 0) { return &list.list[i]; } } return NULL; } const partition_entry_list_t *get_partition_entry_list(void) { return &list; } void partition_init(unsigned int image_id) { load_partition_table(image_id); }