1 /* 2 * Copyright (c) 2016-2025, Arm Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <inttypes.h> 9 #include <stdio.h> 10 #include <string.h> 11 12 #include <common/debug.h> 13 #include <common/tf_crc32.h> 14 #include <drivers/io/io_storage.h> 15 #include <drivers/partition/efi.h> 16 #include <drivers/partition/partition.h> 17 #include <drivers/partition/gpt.h> 18 #include <drivers/partition/mbr.h> 19 #include <plat/common/platform.h> 20 21 static uint8_t mbr_sector[PLAT_PARTITION_BLOCK_SIZE]; 22 static partition_entry_list_t list; 23 24 #if LOG_LEVEL >= LOG_LEVEL_VERBOSE 25 static void dump_entries(int num) 26 { 27 char name[EFI_NAMELEN]; 28 int i, j, len; 29 30 VERBOSE("Partition table with %d entries:\n", num); 31 for (i = 0; i < num; i++) { 32 len = snprintf(name, EFI_NAMELEN, "%s", list.list[i].name); 33 for (j = 0; j < EFI_NAMELEN - len - 1; j++) { 34 name[len + j] = ' '; 35 } 36 name[EFI_NAMELEN - 1] = '\0'; 37 VERBOSE("%d: %s %" PRIx64 "-%" PRIx64 "\n", i + 1, name, list.list[i].start, 38 list.list[i].start + list.list[i].length - 4); 39 } 40 } 41 #else 42 #define dump_entries(num) ((void)num) 43 #endif 44 45 /* 46 * Load the first sector that carries MBR header. 47 * The MBR boot signature should be always valid whether it's MBR or GPT. 48 */ 49 static int load_mbr_header(uintptr_t image_handle, mbr_entry_t *mbr_entry) 50 { 51 size_t bytes_read; 52 int result; 53 mbr_entry_t tmp; 54 55 assert(mbr_entry != NULL); 56 /* MBR partition table is in LBA0. */ 57 result = io_seek(image_handle, IO_SEEK_SET, MBR_OFFSET); 58 if (result != 0) { 59 VERBOSE("Failed to seek (%i)\n", result); 60 return result; 61 } 62 result = io_read(image_handle, (uintptr_t)&mbr_sector, 63 PLAT_PARTITION_BLOCK_SIZE, &bytes_read); 64 if ((result != 0) || (bytes_read != PLAT_PARTITION_BLOCK_SIZE)) { 65 VERBOSE("Failed to read data (%i)\n", result); 66 return result; 67 } 68 69 /* Check MBR boot signature. */ 70 if ((mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 2] != MBR_SIGNATURE_FIRST) || 71 (mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 1] != MBR_SIGNATURE_SECOND)) { 72 VERBOSE("MBR boot signature failure\n"); 73 return -ENOENT; 74 } 75 76 memcpy(&tmp, mbr_sector + MBR_PRIMARY_ENTRY_OFFSET, sizeof(tmp)); 77 78 if ((tmp.sector_nums == 0) || (tmp.sector_nums == UINT32_MAX)) { 79 VERBOSE("MBR header entry has an invalid number of sectors\n"); 80 return -EINVAL; 81 } 82 83 memcpy(mbr_entry, &tmp, sizeof(mbr_entry_t)); 84 return 0; 85 } 86 87 /* 88 * Load GPT header and check the GPT signature and header CRC. 89 * If partition numbers could be found, check & update it. 90 */ 91 static int load_gpt_header(uintptr_t image_handle, size_t header_offset, 92 gpt_header_t *header) 93 { 94 size_t bytes_read; 95 int result; 96 uint32_t header_crc, calc_crc; 97 98 result = io_seek(image_handle, IO_SEEK_SET, header_offset); 99 if (result != 0) { 100 VERBOSE("Failed to seek into the GPT image at offset (%zu)\n", 101 header_offset); 102 return result; 103 } 104 result = io_read(image_handle, (uintptr_t)header, 105 sizeof(gpt_header_t), &bytes_read); 106 if ((result != 0) || (sizeof(gpt_header_t) != bytes_read)) { 107 VERBOSE("GPT header read error(%i) or read mismatch occurred," 108 "expected(%zu) and actual(%zu)\n", result, 109 sizeof(gpt_header_t), bytes_read); 110 return result; 111 } 112 if (memcmp(header->signature, GPT_SIGNATURE, 113 sizeof(header->signature)) != 0) { 114 VERBOSE("GPT header signature failure\n"); 115 return -EINVAL; 116 } 117 118 /* 119 * UEFI Spec 2.8 March 2019 Page 119: HeaderCRC32 value is 120 * computed by setting this field to 0, and computing the 121 * 32-bit CRC for HeaderSize bytes. 122 */ 123 header_crc = header->header_crc; 124 header->header_crc = 0U; 125 126 calc_crc = tf_crc32(0U, (uint8_t *)header, sizeof(gpt_header_t)); 127 if (header_crc != calc_crc) { 128 ERROR("Invalid GPT Header CRC: Expected 0x%x but got 0x%x.\n", 129 header_crc, calc_crc); 130 return -EINVAL; 131 } 132 133 header->header_crc = header_crc; 134 135 /* partition numbers can't exceed PLAT_PARTITION_MAX_ENTRIES */ 136 list.entry_count = header->list_num; 137 if (list.entry_count > PLAT_PARTITION_MAX_ENTRIES) { 138 list.entry_count = PLAT_PARTITION_MAX_ENTRIES; 139 } 140 141 return 0; 142 } 143 144 /* 145 * Load a single MBR entry based on details from MBR header. 146 */ 147 static int load_mbr_entry(uintptr_t image_handle, mbr_entry_t *mbr_entry, 148 int part_number) 149 { 150 size_t bytes_read; 151 uintptr_t offset; 152 int result; 153 154 assert(mbr_entry != NULL); 155 /* MBR partition table is in LBA0. */ 156 result = io_seek(image_handle, IO_SEEK_SET, MBR_OFFSET); 157 if (result != 0) { 158 VERBOSE("Failed to seek (%i)\n", result); 159 return result; 160 } 161 result = io_read(image_handle, (uintptr_t)&mbr_sector, 162 PLAT_PARTITION_BLOCK_SIZE, &bytes_read); 163 if (result != 0) { 164 VERBOSE("Failed to read data (%i)\n", result); 165 return result; 166 } 167 168 /* Check MBR boot signature. */ 169 if ((mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 2] != MBR_SIGNATURE_FIRST) || 170 (mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 1] != MBR_SIGNATURE_SECOND)) { 171 VERBOSE("MBR Entry boot signature failure\n"); 172 return -ENOENT; 173 } 174 offset = (uintptr_t)&mbr_sector + 175 MBR_PRIMARY_ENTRY_OFFSET + 176 MBR_PRIMARY_ENTRY_SIZE * part_number; 177 memcpy(mbr_entry, (void *)offset, sizeof(mbr_entry_t)); 178 179 return 0; 180 } 181 182 /* 183 * Load MBR entries based on max number of partition entries. 184 */ 185 static int load_mbr_entries(uintptr_t image_handle) 186 { 187 mbr_entry_t mbr_entry; 188 unsigned int i; 189 190 list.entry_count = MBR_PRIMARY_ENTRY_NUMBER; 191 192 for (i = 0U; i < list.entry_count; i++) { 193 load_mbr_entry(image_handle, &mbr_entry, i); 194 list.list[i].start = mbr_entry.first_lba * 512; 195 list.list[i].length = mbr_entry.sector_nums * 512; 196 list.list[i].name[0] = mbr_entry.type; 197 } 198 199 return 0; 200 } 201 202 /* 203 * Try to read and load a single GPT entry. 204 */ 205 static int load_gpt_entry(uintptr_t image_handle, gpt_entry_t *entry) 206 { 207 size_t bytes_read = 0U; 208 int result; 209 210 assert(entry != NULL); 211 result = io_read(image_handle, (uintptr_t)entry, sizeof(gpt_entry_t), 212 &bytes_read); 213 if ((result != 0) || (sizeof(gpt_entry_t) != bytes_read)) { 214 VERBOSE("GPT Entry read error(%i) or read mismatch occurred," 215 "expected(%zu) and actual(%zu)\n", result, 216 sizeof(gpt_entry_t), bytes_read); 217 return -EINVAL; 218 } 219 220 return result; 221 } 222 223 /* 224 * Retrieve each entry in the partition table, parse the data from each 225 * entry and store them in the list of partition table entries. 226 */ 227 static int load_partition_gpt(uintptr_t image_handle, gpt_header_t header) 228 { 229 const signed long long gpt_entry_offset = LBA(header.part_lba); 230 gpt_entry_t entry; 231 int result; 232 unsigned int i; 233 uint32_t calc_crc = 0U; 234 235 result = io_seek(image_handle, IO_SEEK_SET, gpt_entry_offset); 236 if (result != 0) { 237 VERBOSE("Failed to seek (%i), Failed loading GPT partition" 238 "table entries\n", result); 239 return result; 240 } 241 242 for (i = 0U; i < list.entry_count; i++) { 243 result = load_gpt_entry(image_handle, &entry); 244 if (result != 0) { 245 VERBOSE("Failed to load gpt entry data(%u) error is (%i)\n", 246 i, result); 247 return result; 248 } 249 250 result = parse_gpt_entry(&entry, &list.list[i]); 251 if (result != 0) { 252 result = io_seek(image_handle, IO_SEEK_SET, 253 (gpt_entry_offset + (i * sizeof(gpt_entry_t)))); 254 if (result != 0) { 255 VERBOSE("Failed to seek (%i)\n", result); 256 return result; 257 } 258 break; 259 } 260 261 /* 262 * Calculate CRC of Partition entry array to compare with CRC 263 * value in header 264 */ 265 calc_crc = tf_crc32(calc_crc, (uint8_t *)&entry, sizeof(gpt_entry_t)); 266 } 267 if (i == 0) { 268 VERBOSE("No Valid GPT Entries found\n"); 269 return -EINVAL; 270 } 271 272 /* 273 * Only records the valid partition number that is loaded from 274 * partition table. 275 */ 276 list.entry_count = i; 277 dump_entries(list.entry_count); 278 279 /* 280 * If there are less valid entries than the possible number of entries 281 * from the header, continue to load the partition entry table to 282 * calculate the full CRC in order to check against the partition CRC 283 * from the header for validation. 284 */ 285 for (; i < header.list_num; i++) { 286 result = load_gpt_entry(image_handle, &entry); 287 if (result != 0) { 288 VERBOSE("Failed to load gpt entry data(%u) error is (%i)\n", 289 i, result); 290 return result; 291 } 292 293 calc_crc = tf_crc32(calc_crc, (uint8_t *)&entry, sizeof(gpt_entry_t)); 294 } 295 296 if (header.part_crc != calc_crc) { 297 ERROR("Invalid GPT Partition Array Entry CRC: Expected 0x%x" 298 " but got 0x%x.\n", header.part_crc, calc_crc); 299 return -EINVAL; 300 } 301 302 return 0; 303 } 304 305 /* 306 * Try retrieving and parsing the backup-GPT header and backup GPT entries. 307 * Last 33 blocks contains the backup-GPT entries and header. 308 */ 309 static int load_backup_gpt(unsigned int image_id, unsigned int sector_nums) 310 { 311 int result; 312 gpt_header_t header; 313 size_t gpt_header_offset; 314 uintptr_t dev_handle, image_spec, image_handle; 315 io_block_spec_t *block_spec; 316 int part_num_entries; 317 318 result = plat_get_image_source(image_id, &dev_handle, &image_spec); 319 if (result != 0) { 320 VERBOSE("Failed to obtain reference to image id=%u (%i)\n", 321 image_id, result); 322 return result; 323 } 324 325 block_spec = (io_block_spec_t *)image_spec; 326 /* 327 * We need to read 32 blocks of GPT entries and one block of GPT header 328 * try mapping only last 33 last blocks from the image to read the 329 * Backup-GPT header and its entries. 330 */ 331 part_num_entries = (PLAT_PARTITION_MAX_ENTRIES / 4); 332 /* Move the offset base to LBA-33 */ 333 block_spec->offset += LBA(sector_nums - part_num_entries); 334 /* 335 * Set length as LBA-33, 32 blocks of backup-GPT entries and one 336 * block of backup-GPT header. 337 */ 338 block_spec->length = LBA(part_num_entries + 1); 339 340 result = io_open(dev_handle, image_spec, &image_handle); 341 if (result != 0) { 342 VERBOSE("Failed to access image id (%i)\n", result); 343 return result; 344 } 345 346 INFO("Trying to retrieve back-up GPT header\n"); 347 /* Last block is backup-GPT header, after the end of GPT entries */ 348 gpt_header_offset = LBA(part_num_entries); 349 result = load_gpt_header(image_handle, gpt_header_offset, &header); 350 if ((result != 0) || (header.part_lba == 0)) { 351 ERROR("Failed to retrieve Backup GPT header," 352 "Partition maybe corrupted\n"); 353 goto out; 354 } 355 356 /* 357 * Note we mapped last 33 blocks(LBA-33), first block here starts with 358 * entries while last block was header. 359 */ 360 header.part_lba = 0; 361 result = load_partition_gpt(image_handle, header); 362 363 out: 364 io_close(image_handle); 365 return result; 366 } 367 368 /* 369 * Load a GPT partition, Try retrieving and parsing the primary GPT header, 370 * if its corrupted try loading backup GPT header and then retrieve list 371 * of partition table entries found from the GPT. 372 */ 373 static int load_primary_gpt(uintptr_t image_handle, unsigned int first_lba) 374 { 375 int result; 376 size_t gpt_header_offset; 377 gpt_header_t header; 378 379 /* Try to load Primary GPT header from LBA1 */ 380 gpt_header_offset = LBA(first_lba); 381 result = load_gpt_header(image_handle, gpt_header_offset, &header); 382 if ((result != 0) || (header.part_lba == 0)) { 383 VERBOSE("Failed to retrieve Primary GPT header," 384 "trying to retrieve back-up GPT header\n"); 385 return result; 386 } 387 388 return load_partition_gpt(image_handle, header); 389 } 390 391 static void handle_gpt_corruption(void) 392 { 393 uint8_t flags; 394 395 if ((plat_log_gpt_ptr == NULL) || 396 (plat_log_gpt_ptr->plat_set_gpt_corruption == NULL)) { 397 return; 398 } 399 400 flags = plat_log_gpt_ptr->gpt_corrupted_info | PRIMARY_GPT_CORRUPTED; 401 plat_log_gpt_ptr->plat_set_gpt_corruption((uintptr_t)&plat_log_gpt_ptr->gpt_corrupted_info, 402 flags); 403 } 404 405 /* 406 * Load the partition table info based on the image id provided. 407 */ 408 int load_partition_table(unsigned int image_id) 409 { 410 uintptr_t dev_handle, image_handle, image_spec = 0; 411 mbr_entry_t mbr_entry = {0}; 412 int result; 413 414 result = plat_get_image_source(image_id, &dev_handle, &image_spec); 415 if (result != 0) { 416 VERBOSE("Failed to obtain reference to image id=%u (%i)\n", 417 image_id, result); 418 return result; 419 } 420 421 result = io_open(dev_handle, image_spec, &image_handle); 422 if (result != 0) { 423 VERBOSE("Failed to access image id=%u (%i)\n", image_id, result); 424 return result; 425 } 426 427 result = load_mbr_header(image_handle, &mbr_entry); 428 if (result != 0) { 429 VERBOSE("Failed to access image id=%u (%i)\n", image_id, result); 430 goto out; 431 } 432 if (mbr_entry.type == PARTITION_TYPE_GPT) { 433 if (mbr_entry.first_lba != 1U) { 434 VERBOSE("MBR header may have an invalid first LBA\n"); 435 return -EINVAL; 436 } 437 438 result = load_primary_gpt(image_handle, mbr_entry.first_lba); 439 if (result != 0) { 440 io_close(image_handle); 441 handle_gpt_corruption(); 442 return load_backup_gpt(BKUP_GPT_IMAGE_ID, 443 mbr_entry.sector_nums); 444 } 445 } else { 446 result = load_mbr_entries(image_handle); 447 } 448 449 out: 450 io_close(image_handle); 451 return result; 452 } 453 454 /* 455 * Try retrieving a partition table entry based on the name of the partition. 456 */ 457 const partition_entry_t *get_partition_entry(const char *name) 458 { 459 unsigned int i; 460 461 for (i = 0U; i < list.entry_count; i++) { 462 if (strcmp(name, list.list[i].name) == 0) { 463 return &list.list[i]; 464 } 465 } 466 return NULL; 467 } 468 469 /* 470 * Try retrieving a partition table entry based on the partition type GUID. 471 */ 472 const partition_entry_t *get_partition_entry_by_type( 473 const struct efi_guid *type_guid) 474 { 475 unsigned int i; 476 477 for (i = 0U; i < list.entry_count; i++) { 478 if (guidcmp(type_guid, &list.list[i].type_guid) == 0) { 479 return &list.list[i]; 480 } 481 } 482 483 return NULL; 484 } 485 486 /* 487 * Try retrieving a partition table entry based on the unique partition GUID. 488 */ 489 const partition_entry_t *get_partition_entry_by_guid( 490 const struct efi_guid *part_guid) 491 { 492 unsigned int i; 493 494 for (i = 0U; i < list.entry_count; i++) { 495 if (guidcmp(part_guid, &list.list[i].part_guid) == 0) { 496 return &list.list[i]; 497 } 498 } 499 500 return NULL; 501 } 502 503 /* 504 * Return entry to the list of partition table entries. 505 */ 506 const partition_entry_list_t *get_partition_entry_list(void) 507 { 508 return &list; 509 } 510 511 /* 512 * Try loading partition table info for the given image ID. 513 */ 514 void partition_init(unsigned int image_id) 515 { 516 int ret; 517 518 ret = load_partition_table(image_id); 519 if (ret != 0) { 520 ERROR("Failed to parse partition with image id = %u\n", 521 image_id); 522 } 523 } 524 525 /* 526 * Load a GPT based image. 527 */ 528 int gpt_partition_init(void) 529 { 530 return load_partition_table(GPT_IMAGE_ID); 531 } 532