1201b66b7SHaojian Zhuang /* 2*fce8a70eSGovindraj Raja * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. 3201b66b7SHaojian Zhuang * 482cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause 5201b66b7SHaojian Zhuang */ 6201b66b7SHaojian Zhuang 7201b66b7SHaojian Zhuang #include <assert.h> 84ef449c1SManish Pandey #include <inttypes.h> 939b6cc66SAntonio Nino Diaz #include <stdio.h> 10201b66b7SHaojian Zhuang #include <string.h> 11201b66b7SHaojian Zhuang 1209d40e0eSAntonio Nino Diaz #include <common/debug.h> 13a283d19fSRohit Ner #include <common/tf_crc32.h> 1409d40e0eSAntonio Nino Diaz #include <drivers/io/io_storage.h> 153cb10655SSughosh Ganu #include <drivers/partition/efi.h> 1609d40e0eSAntonio Nino Diaz #include <drivers/partition/partition.h> 1709d40e0eSAntonio Nino Diaz #include <drivers/partition/gpt.h> 1809d40e0eSAntonio Nino Diaz #include <drivers/partition/mbr.h> 1909d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 2009d40e0eSAntonio Nino Diaz 21f8631f51SHaojian Zhuang static uint8_t mbr_sector[PLAT_PARTITION_BLOCK_SIZE]; 2298228529SFlorian La Roche static partition_entry_list_t list; 23201b66b7SHaojian Zhuang 24201b66b7SHaojian Zhuang #if LOG_LEVEL >= LOG_LEVEL_VERBOSE 25201b66b7SHaojian Zhuang static void dump_entries(int num) 26201b66b7SHaojian Zhuang { 27201b66b7SHaojian Zhuang char name[EFI_NAMELEN]; 28201b66b7SHaojian Zhuang int i, j, len; 29201b66b7SHaojian Zhuang 30201b66b7SHaojian Zhuang VERBOSE("Partition table with %d entries:\n", num); 31201b66b7SHaojian Zhuang for (i = 0; i < num; i++) { 3239b6cc66SAntonio Nino Diaz len = snprintf(name, EFI_NAMELEN, "%s", list.list[i].name); 33201b66b7SHaojian Zhuang for (j = 0; j < EFI_NAMELEN - len - 1; j++) { 34201b66b7SHaojian Zhuang name[len + j] = ' '; 35201b66b7SHaojian Zhuang } 36201b66b7SHaojian Zhuang name[EFI_NAMELEN - 1] = '\0'; 374ef449c1SManish Pandey VERBOSE("%d: %s %" PRIx64 "-%" PRIx64 "\n", i + 1, name, list.list[i].start, 38201b66b7SHaojian Zhuang list.list[i].start + list.list[i].length - 4); 39201b66b7SHaojian Zhuang } 40201b66b7SHaojian Zhuang } 41201b66b7SHaojian Zhuang #else 42201b66b7SHaojian Zhuang #define dump_entries(num) ((void)num) 43201b66b7SHaojian Zhuang #endif 44201b66b7SHaojian Zhuang 45201b66b7SHaojian Zhuang /* 46201b66b7SHaojian Zhuang * Load the first sector that carries MBR header. 47201b66b7SHaojian Zhuang * The MBR boot signature should be always valid whether it's MBR or GPT. 48201b66b7SHaojian Zhuang */ 49201b66b7SHaojian Zhuang static int load_mbr_header(uintptr_t image_handle, mbr_entry_t *mbr_entry) 50201b66b7SHaojian Zhuang { 51201b66b7SHaojian Zhuang size_t bytes_read; 52201b66b7SHaojian Zhuang int result; 53*fce8a70eSGovindraj Raja mbr_entry_t *tmp; 54201b66b7SHaojian Zhuang 55201b66b7SHaojian Zhuang assert(mbr_entry != NULL); 56201b66b7SHaojian Zhuang /* MBR partition table is in LBA0. */ 57201b66b7SHaojian Zhuang result = io_seek(image_handle, IO_SEEK_SET, MBR_OFFSET); 58201b66b7SHaojian Zhuang if (result != 0) { 59201b66b7SHaojian Zhuang WARN("Failed to seek (%i)\n", result); 60201b66b7SHaojian Zhuang return result; 61201b66b7SHaojian Zhuang } 62201b66b7SHaojian Zhuang result = io_read(image_handle, (uintptr_t)&mbr_sector, 63f8631f51SHaojian Zhuang PLAT_PARTITION_BLOCK_SIZE, &bytes_read); 64*fce8a70eSGovindraj Raja if ((result != 0) || (bytes_read != PLAT_PARTITION_BLOCK_SIZE)) { 65201b66b7SHaojian Zhuang WARN("Failed to read data (%i)\n", result); 66201b66b7SHaojian Zhuang return result; 67201b66b7SHaojian Zhuang } 68201b66b7SHaojian Zhuang 69201b66b7SHaojian Zhuang /* Check MBR boot signature. */ 70f8631f51SHaojian Zhuang if ((mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 2] != MBR_SIGNATURE_FIRST) || 71f8631f51SHaojian Zhuang (mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 1] != MBR_SIGNATURE_SECOND)) { 72201b66b7SHaojian Zhuang return -ENOENT; 73201b66b7SHaojian Zhuang } 74*fce8a70eSGovindraj Raja 75*fce8a70eSGovindraj Raja tmp = (mbr_entry_t *)(&mbr_sector[MBR_PRIMARY_ENTRY_OFFSET]); 76*fce8a70eSGovindraj Raja 77*fce8a70eSGovindraj Raja if (tmp->first_lba != 1) { 78*fce8a70eSGovindraj Raja WARN("MBR header may have an invalid first LBA\n"); 79*fce8a70eSGovindraj Raja return -EINVAL; 80*fce8a70eSGovindraj Raja } 81*fce8a70eSGovindraj Raja 82*fce8a70eSGovindraj Raja if ((tmp->sector_nums == 0) || (tmp->sector_nums == UINT32_MAX)) { 83*fce8a70eSGovindraj Raja WARN("MBR header entry has an invalid number of sectors\n"); 84*fce8a70eSGovindraj Raja return -EINVAL; 85*fce8a70eSGovindraj Raja } 86*fce8a70eSGovindraj Raja 87*fce8a70eSGovindraj Raja memcpy(mbr_entry, tmp, sizeof(mbr_entry_t)); 88201b66b7SHaojian Zhuang return 0; 89201b66b7SHaojian Zhuang } 90201b66b7SHaojian Zhuang 91201b66b7SHaojian Zhuang /* 92a283d19fSRohit Ner * Load GPT header and check the GPT signature and header CRC. 938aabea33SPaul Beesley * If partition numbers could be found, check & update it. 94201b66b7SHaojian Zhuang */ 95*fce8a70eSGovindraj Raja static int load_gpt_header(uintptr_t image_handle, size_t header_offset, 96*fce8a70eSGovindraj Raja unsigned long long *part_lba) 97201b66b7SHaojian Zhuang { 98201b66b7SHaojian Zhuang gpt_header_t header; 99201b66b7SHaojian Zhuang size_t bytes_read; 100201b66b7SHaojian Zhuang int result; 101a283d19fSRohit Ner uint32_t header_crc, calc_crc; 102201b66b7SHaojian Zhuang 103*fce8a70eSGovindraj Raja result = io_seek(image_handle, IO_SEEK_SET, header_offset); 104201b66b7SHaojian Zhuang if (result != 0) { 105201b66b7SHaojian Zhuang return result; 106201b66b7SHaojian Zhuang } 107201b66b7SHaojian Zhuang result = io_read(image_handle, (uintptr_t)&header, 108201b66b7SHaojian Zhuang sizeof(gpt_header_t), &bytes_read); 109201b66b7SHaojian Zhuang if ((result != 0) || (sizeof(gpt_header_t) != bytes_read)) { 110201b66b7SHaojian Zhuang return result; 111201b66b7SHaojian Zhuang } 112201b66b7SHaojian Zhuang if (memcmp(header.signature, GPT_SIGNATURE, 113201b66b7SHaojian Zhuang sizeof(header.signature)) != 0) { 114201b66b7SHaojian Zhuang return -EINVAL; 115201b66b7SHaojian Zhuang } 116201b66b7SHaojian Zhuang 117a283d19fSRohit Ner /* 118a283d19fSRohit Ner * UEFI Spec 2.8 March 2019 Page 119: HeaderCRC32 value is 119a283d19fSRohit Ner * computed by setting this field to 0, and computing the 120a283d19fSRohit Ner * 32-bit CRC for HeaderSize bytes. 121a283d19fSRohit Ner */ 122a283d19fSRohit Ner header_crc = header.header_crc; 123a283d19fSRohit Ner header.header_crc = 0U; 124a283d19fSRohit Ner 125*fce8a70eSGovindraj Raja calc_crc = tf_crc32(0U, (uint8_t *)&header, sizeof(gpt_header_t)); 126a283d19fSRohit Ner if (header_crc != calc_crc) { 127a283d19fSRohit Ner ERROR("Invalid GPT Header CRC: Expected 0x%x but got 0x%x.\n", 128a283d19fSRohit Ner header_crc, calc_crc); 129a283d19fSRohit Ner return -EINVAL; 130a283d19fSRohit Ner } 131a283d19fSRohit Ner 132a283d19fSRohit Ner header.header_crc = header_crc; 133a283d19fSRohit Ner 134201b66b7SHaojian Zhuang /* partition numbers can't exceed PLAT_PARTITION_MAX_ENTRIES */ 135201b66b7SHaojian Zhuang list.entry_count = header.list_num; 136201b66b7SHaojian Zhuang if (list.entry_count > PLAT_PARTITION_MAX_ENTRIES) { 137201b66b7SHaojian Zhuang list.entry_count = PLAT_PARTITION_MAX_ENTRIES; 138201b66b7SHaojian Zhuang } 139*fce8a70eSGovindraj Raja 140*fce8a70eSGovindraj Raja *part_lba = header.part_lba; 141201b66b7SHaojian Zhuang return 0; 142201b66b7SHaojian Zhuang } 143201b66b7SHaojian Zhuang 14430f833caSLoh Tien Hock static int load_mbr_entry(uintptr_t image_handle, mbr_entry_t *mbr_entry, 14530f833caSLoh Tien Hock int part_number) 14630f833caSLoh Tien Hock { 14730f833caSLoh Tien Hock size_t bytes_read; 14830f833caSLoh Tien Hock uintptr_t offset; 14930f833caSLoh Tien Hock int result; 15030f833caSLoh Tien Hock 15130f833caSLoh Tien Hock assert(mbr_entry != NULL); 15230f833caSLoh Tien Hock /* MBR partition table is in LBA0. */ 15330f833caSLoh Tien Hock result = io_seek(image_handle, IO_SEEK_SET, MBR_OFFSET); 15430f833caSLoh Tien Hock if (result != 0) { 15530f833caSLoh Tien Hock WARN("Failed to seek (%i)\n", result); 15630f833caSLoh Tien Hock return result; 15730f833caSLoh Tien Hock } 15830f833caSLoh Tien Hock result = io_read(image_handle, (uintptr_t)&mbr_sector, 159f8631f51SHaojian Zhuang PLAT_PARTITION_BLOCK_SIZE, &bytes_read); 16030f833caSLoh Tien Hock if (result != 0) { 16130f833caSLoh Tien Hock WARN("Failed to read data (%i)\n", result); 16230f833caSLoh Tien Hock return result; 16330f833caSLoh Tien Hock } 16430f833caSLoh Tien Hock 16530f833caSLoh Tien Hock /* Check MBR boot signature. */ 166f8631f51SHaojian Zhuang if ((mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 2] != MBR_SIGNATURE_FIRST) || 167f8631f51SHaojian Zhuang (mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 1] != MBR_SIGNATURE_SECOND)) { 16830f833caSLoh Tien Hock return -ENOENT; 16930f833caSLoh Tien Hock } 17030f833caSLoh Tien Hock offset = (uintptr_t)&mbr_sector + 17130f833caSLoh Tien Hock MBR_PRIMARY_ENTRY_OFFSET + 17230f833caSLoh Tien Hock MBR_PRIMARY_ENTRY_SIZE * part_number; 17330f833caSLoh Tien Hock memcpy(mbr_entry, (void *)offset, sizeof(mbr_entry_t)); 17430f833caSLoh Tien Hock 17530f833caSLoh Tien Hock return 0; 17630f833caSLoh Tien Hock } 17730f833caSLoh Tien Hock 17830f833caSLoh Tien Hock static int load_mbr_entries(uintptr_t image_handle) 17930f833caSLoh Tien Hock { 18030f833caSLoh Tien Hock mbr_entry_t mbr_entry; 18130f833caSLoh Tien Hock int i; 18230f833caSLoh Tien Hock 18330f833caSLoh Tien Hock list.entry_count = MBR_PRIMARY_ENTRY_NUMBER; 18430f833caSLoh Tien Hock 18530f833caSLoh Tien Hock for (i = 0; i < list.entry_count; i++) { 18630f833caSLoh Tien Hock load_mbr_entry(image_handle, &mbr_entry, i); 18730f833caSLoh Tien Hock list.list[i].start = mbr_entry.first_lba * 512; 18830f833caSLoh Tien Hock list.list[i].length = mbr_entry.sector_nums * 512; 18930f833caSLoh Tien Hock list.list[i].name[0] = mbr_entry.type; 19030f833caSLoh Tien Hock } 19130f833caSLoh Tien Hock 19230f833caSLoh Tien Hock return 0; 19330f833caSLoh Tien Hock } 19430f833caSLoh Tien Hock 195201b66b7SHaojian Zhuang static int load_gpt_entry(uintptr_t image_handle, gpt_entry_t *entry) 196201b66b7SHaojian Zhuang { 197201b66b7SHaojian Zhuang size_t bytes_read; 198201b66b7SHaojian Zhuang int result; 199201b66b7SHaojian Zhuang 200201b66b7SHaojian Zhuang assert(entry != NULL); 201201b66b7SHaojian Zhuang result = io_read(image_handle, (uintptr_t)entry, sizeof(gpt_entry_t), 202201b66b7SHaojian Zhuang &bytes_read); 203201b66b7SHaojian Zhuang if (sizeof(gpt_entry_t) != bytes_read) 204201b66b7SHaojian Zhuang return -EINVAL; 205201b66b7SHaojian Zhuang return result; 206201b66b7SHaojian Zhuang } 207201b66b7SHaojian Zhuang 208201b66b7SHaojian Zhuang static int verify_partition_gpt(uintptr_t image_handle) 209201b66b7SHaojian Zhuang { 210201b66b7SHaojian Zhuang gpt_entry_t entry; 211201b66b7SHaojian Zhuang int result, i; 212201b66b7SHaojian Zhuang 213201b66b7SHaojian Zhuang for (i = 0; i < list.entry_count; i++) { 214201b66b7SHaojian Zhuang result = load_gpt_entry(image_handle, &entry); 215201b66b7SHaojian Zhuang assert(result == 0); 216201b66b7SHaojian Zhuang result = parse_gpt_entry(&entry, &list.list[i]); 217201b66b7SHaojian Zhuang if (result != 0) { 218201b66b7SHaojian Zhuang break; 219201b66b7SHaojian Zhuang } 220201b66b7SHaojian Zhuang } 221201b66b7SHaojian Zhuang if (i == 0) { 222201b66b7SHaojian Zhuang return -EINVAL; 223201b66b7SHaojian Zhuang } 224201b66b7SHaojian Zhuang /* 225201b66b7SHaojian Zhuang * Only records the valid partition number that is loaded from 226201b66b7SHaojian Zhuang * partition table. 227201b66b7SHaojian Zhuang */ 228201b66b7SHaojian Zhuang list.entry_count = i; 229201b66b7SHaojian Zhuang dump_entries(list.entry_count); 230201b66b7SHaojian Zhuang 231201b66b7SHaojian Zhuang return 0; 232201b66b7SHaojian Zhuang } 233201b66b7SHaojian Zhuang 234201b66b7SHaojian Zhuang int load_partition_table(unsigned int image_id) 235201b66b7SHaojian Zhuang { 236201b66b7SHaojian Zhuang uintptr_t dev_handle, image_handle, image_spec = 0; 237201b66b7SHaojian Zhuang mbr_entry_t mbr_entry; 238201b66b7SHaojian Zhuang int result; 239*fce8a70eSGovindraj Raja size_t gpt_header_offset; 240*fce8a70eSGovindraj Raja unsigned long long part_lba = 0, gpt_entry_offset = 0; 241201b66b7SHaojian Zhuang 242201b66b7SHaojian Zhuang result = plat_get_image_source(image_id, &dev_handle, &image_spec); 243201b66b7SHaojian Zhuang if (result != 0) { 244201b66b7SHaojian Zhuang WARN("Failed to obtain reference to image id=%u (%i)\n", 245201b66b7SHaojian Zhuang image_id, result); 246201b66b7SHaojian Zhuang return result; 247201b66b7SHaojian Zhuang } 248201b66b7SHaojian Zhuang 249201b66b7SHaojian Zhuang result = io_open(dev_handle, image_spec, &image_handle); 250201b66b7SHaojian Zhuang if (result != 0) { 251201b66b7SHaojian Zhuang WARN("Failed to access image id=%u (%i)\n", image_id, result); 252201b66b7SHaojian Zhuang return result; 253201b66b7SHaojian Zhuang } 254201b66b7SHaojian Zhuang 255201b66b7SHaojian Zhuang result = load_mbr_header(image_handle, &mbr_entry); 256201b66b7SHaojian Zhuang if (result != 0) { 257201b66b7SHaojian Zhuang WARN("Failed to access image id=%u (%i)\n", image_id, result); 258201b66b7SHaojian Zhuang return result; 259201b66b7SHaojian Zhuang } 260201b66b7SHaojian Zhuang if (mbr_entry.type == PARTITION_TYPE_GPT) { 261*fce8a70eSGovindraj Raja /* Try to load GPT header from LBA-1 */ 262*fce8a70eSGovindraj Raja gpt_header_offset = mbr_entry.first_lba * PLAT_PARTITION_BLOCK_SIZE; 263*fce8a70eSGovindraj Raja result = load_gpt_header(image_handle, gpt_header_offset, &part_lba); 264*fce8a70eSGovindraj Raja if (result != 0 || part_lba == 0) { 265*fce8a70eSGovindraj Raja WARN("Failed to retrieve Primary GPT header\n"); 266*fce8a70eSGovindraj Raja return result; 267*fce8a70eSGovindraj Raja } 268*fce8a70eSGovindraj Raja 269*fce8a70eSGovindraj Raja gpt_entry_offset = part_lba * PLAT_PARTITION_BLOCK_SIZE; 270*fce8a70eSGovindraj Raja result = io_seek(image_handle, IO_SEEK_SET, gpt_entry_offset); 271*fce8a70eSGovindraj Raja if (result != 0) { 272*fce8a70eSGovindraj Raja WARN("Failed to seek (%i)," 273*fce8a70eSGovindraj Raja "Failed loading GPT partition table entries\n", 274*fce8a70eSGovindraj Raja result); 275*fce8a70eSGovindraj Raja return result; 276*fce8a70eSGovindraj Raja } 277201b66b7SHaojian Zhuang result = verify_partition_gpt(image_handle); 278201b66b7SHaojian Zhuang } else { 27930f833caSLoh Tien Hock result = load_mbr_entries(image_handle); 280201b66b7SHaojian Zhuang } 28130f833caSLoh Tien Hock 282201b66b7SHaojian Zhuang io_close(image_handle); 283201b66b7SHaojian Zhuang return result; 284201b66b7SHaojian Zhuang } 285201b66b7SHaojian Zhuang 286201b66b7SHaojian Zhuang const partition_entry_t *get_partition_entry(const char *name) 287201b66b7SHaojian Zhuang { 288201b66b7SHaojian Zhuang int i; 289201b66b7SHaojian Zhuang 290201b66b7SHaojian Zhuang for (i = 0; i < list.entry_count; i++) { 291201b66b7SHaojian Zhuang if (strcmp(name, list.list[i].name) == 0) { 292201b66b7SHaojian Zhuang return &list.list[i]; 293201b66b7SHaojian Zhuang } 294201b66b7SHaojian Zhuang } 295201b66b7SHaojian Zhuang return NULL; 296201b66b7SHaojian Zhuang } 297201b66b7SHaojian Zhuang 298564f5d47SLionel Debieve const partition_entry_t *get_partition_entry_by_type(const uuid_t *type_uuid) 299564f5d47SLionel Debieve { 300564f5d47SLionel Debieve int i; 301564f5d47SLionel Debieve 302564f5d47SLionel Debieve for (i = 0; i < list.entry_count; i++) { 303564f5d47SLionel Debieve if (guidcmp(type_uuid, &list.list[i].type_guid) == 0) { 304564f5d47SLionel Debieve return &list.list[i]; 305564f5d47SLionel Debieve } 306564f5d47SLionel Debieve } 307564f5d47SLionel Debieve 308564f5d47SLionel Debieve return NULL; 309564f5d47SLionel Debieve } 310564f5d47SLionel Debieve 3113cb10655SSughosh Ganu const partition_entry_t *get_partition_entry_by_uuid(const uuid_t *part_uuid) 3123cb10655SSughosh Ganu { 3133cb10655SSughosh Ganu int i; 3143cb10655SSughosh Ganu 3153cb10655SSughosh Ganu for (i = 0; i < list.entry_count; i++) { 3163cb10655SSughosh Ganu if (guidcmp(part_uuid, &list.list[i].part_guid) == 0) { 3173cb10655SSughosh Ganu return &list.list[i]; 3183cb10655SSughosh Ganu } 3193cb10655SSughosh Ganu } 3203cb10655SSughosh Ganu 3213cb10655SSughosh Ganu return NULL; 3223cb10655SSughosh Ganu } 3233cb10655SSughosh Ganu 324201b66b7SHaojian Zhuang const partition_entry_list_t *get_partition_entry_list(void) 325201b66b7SHaojian Zhuang { 326201b66b7SHaojian Zhuang return &list; 327201b66b7SHaojian Zhuang } 328201b66b7SHaojian Zhuang 329201b66b7SHaojian Zhuang void partition_init(unsigned int image_id) 330201b66b7SHaojian Zhuang { 331201b66b7SHaojian Zhuang load_partition_table(image_id); 332201b66b7SHaojian Zhuang } 333