1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <dirent.h> 6#include <errno.h> 7#include <fcntl.h> 8 9#include <chromeos-disk-setup/chromeos-disk-setup.h> 10#include <fbl/auto_call.h> 11#include <fbl/function.h> 12#include <fs-management/fvm.h> 13#include <gpt/cros.h> 14#include <lib/fdio/watcher.h> 15#include <zircon/device/device.h> 16#include <zircon/device/skip-block.h> 17#include <zircon/status.h> 18#include <zxcrypt/volume.h> 19 20#include "device-partitioner.h" 21#include "pave-logging.h" 22#include "pave-utils.h" 23 24namespace paver { 25 26bool (*TestBlockFilter)(const fbl::unique_fd&) = nullptr; 27bool (*TestSkipBlockFilter)(const fbl::unique_fd&) = nullptr; 28 29namespace { 30 31bool KernelFilterCallback(const gpt_partition_t& part, fbl::StringPiece partition_name) { 32 const uint8_t kern_type[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE; 33 char cstring_name[GPT_NAME_LEN]; 34 utf16_to_cstring(cstring_name, reinterpret_cast<const uint16_t*>(part.name), GPT_NAME_LEN); 35 return memcmp(part.type, kern_type, GPT_GUID_LEN) == 0 && 36 strncmp(cstring_name, partition_name.data(), partition_name.length()) == 0; 37} 38 39bool FvmFilterCallback(const gpt_partition_t& part) { 40 const uint8_t partition_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 41 return memcmp(part.type, partition_type, GPT_GUID_LEN) == 0; 42} 43 44constexpr size_t ReservedHeaderBlocks(size_t blk_size) { 45 constexpr size_t kReservedEntryBlocks = (16 * 1024); 46 return (kReservedEntryBlocks + 2 * blk_size) / blk_size; 47}; 48 49constexpr char kGptDriverName[] = "/boot/driver/gpt.so"; 50constexpr char kFvmPartitionName[] = "fvm"; 51 52// Helper function to auto-deduce type. 53template <typename T> 54fbl::unique_ptr<T> WrapUnique(T* ptr) { 55 return fbl::unique_ptr<T>(ptr); 56} 57 58 59zx_status_t OpenPartition(const char* path, 60 fbl::Function<bool(const fbl::unique_fd&)> should_filter_file, 61 zx_duration_t timeout, fbl::unique_fd* out_partition) { 62 ZX_ASSERT(path != nullptr); 63 64 struct CallbackInfo { 65 fbl::unique_fd* out_partition; 66 fbl::Function<bool(const fbl::unique_fd&)> should_filter_file; 67 }; 68 69 CallbackInfo info = { 70 .out_partition = out_partition, 71 .should_filter_file = fbl::move(should_filter_file), 72 }; 73 74 auto cb = [](int dirfd, int event, const char* filename, void* cookie) { 75 if (event != WATCH_EVENT_ADD_FILE) { 76 return ZX_OK; 77 } 78 if ((strcmp(filename, ".") == 0) || strcmp(filename, "..") == 0) { 79 return ZX_OK; 80 } 81 fbl::unique_fd devfd(openat(dirfd, filename, O_RDWR)); 82 if (!devfd) { 83 return ZX_OK; 84 } 85 auto info = static_cast<CallbackInfo*>(cookie); 86 if (info->should_filter_file(devfd)) { 87 return ZX_OK; 88 } 89 if (info->out_partition) { 90 *(info->out_partition) = fbl::move(devfd); 91 } 92 return ZX_ERR_STOP; 93 }; 94 95 DIR* dir = opendir(path); 96 if (dir == nullptr) { 97 return ZX_ERR_IO; 98 } 99 const auto closer = fbl::MakeAutoCall([&dir]() { closedir(dir); }); 100 101 zx_time_t deadline = zx_deadline_after(timeout); 102 if (fdio_watch_directory(dirfd(dir), cb, deadline, &info) != ZX_ERR_STOP) { 103 return ZX_ERR_NOT_FOUND; 104 } 105 return ZX_OK; 106} 107 108constexpr char kBlockDevPath[] = "/dev/class/block/"; 109 110zx_status_t OpenBlockPartition(const uint8_t* unique_guid, const uint8_t* type_guid, 111 zx_duration_t timeout, fbl::unique_fd* out_fd) { 112 ZX_ASSERT(unique_guid || type_guid); 113 114 auto cb = [&](const fbl::unique_fd& fd) { 115 if (TestBlockFilter && TestBlockFilter(fd)) { 116 return true; 117 } 118 uint8_t buf[GUID_LEN]; 119 if (type_guid) { 120 if (ioctl_block_get_type_guid(fd.get(), buf, sizeof(buf)) < 0 || 121 memcmp(buf, type_guid, GUID_LEN) != 0) { 122 return true; 123 } 124 } 125 if (unique_guid) { 126 if (ioctl_block_get_partition_guid(fd.get(), buf, sizeof(buf)) < 0 || 127 memcmp(buf, unique_guid, GUID_LEN) != 0) { 128 return true; 129 } 130 } 131 return false; 132 }; 133 134 return OpenPartition(kBlockDevPath, cb, timeout, out_fd); 135} 136 137constexpr char kSkipBlockDevPath[] = "/dev/class/skip-block/"; 138 139zx_status_t OpenSkipBlockPartition(const uint8_t* type_guid, zx_duration_t timeout, 140 fbl::unique_fd* out_fd) { 141 ZX_ASSERT(type_guid); 142 143 auto cb = [&](const fbl::unique_fd& fd) { 144 if (TestSkipBlockFilter && TestSkipBlockFilter(fd)) { 145 return true; 146 } 147 skip_block_partition_info_t part_info; 148 if (ioctl_skip_block_get_partition_info(fd.get(), &part_info) < 0 || 149 memcmp(part_info.partition_guid, type_guid, GUID_LEN) != 0) { 150 return true; 151 } 152 return false; 153 }; 154 155 return OpenPartition(kSkipBlockDevPath, cb, timeout, out_fd); 156} 157 158bool HasSkipBlockDevice() { 159 // Our proxy for detected a skip-block device is by checking for the 160 // existence of a device enumerated under the skip-block class. 161 const uint8_t type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE; 162 return OpenSkipBlockPartition(type, ZX_SEC(1), nullptr) == ZX_OK; 163} 164 165// Attempts to open and overwrite the first block of the underlying 166// partition. Does not rebind partition drivers. 167// 168// At most one of |unique_guid| and |type_guid| may be nullptr. 169zx_status_t WipeBlockPartition(const uint8_t* unique_guid, const uint8_t* type_guid) { 170 zx_status_t status = ZX_OK; 171 fbl::unique_fd fd; 172 if ((status = OpenBlockPartition(unique_guid, type_guid, ZX_SEC(3), &fd)) != ZX_OK) { 173 ERROR("Warning: Could not open partition to wipe: %s\n", 174 zx_status_get_string(status)); 175 return status; 176 } 177 178 block_info_t info; 179 ssize_t result = ZX_OK; 180 if ((result = ioctl_block_get_info(fd.get(), &info)) < 0) { 181 status = static_cast<zx_status_t>(result); 182 ERROR("Warning: Could not acquire block info: %s\n", 183 zx_status_get_string(status)); 184 return status; 185 } 186 187 // Overwrite the first block to (hackily) ensure the destroyed partition 188 // doesn't "reappear" in place. 189 char buf[info.block_size]; 190 memset(buf, 0, info.block_size); 191 192 if (pwrite(fd.get(), buf, info.block_size, 0) != info.block_size) { 193 ERROR("Warning: Could not write to block device: %s\n", strerror(errno)); 194 return ZX_ERR_IO; 195 } 196 197 if ((status = FlushBlockDevice(fd)) != ZX_OK) { 198 ERROR("Warning: Failed to synchronize block device: %s\n", 199 zx_status_get_string(status)); 200 return status; 201 } 202 203 return ZX_OK; 204} 205 206} // namespace 207 208fbl::unique_ptr<DevicePartitioner> DevicePartitioner::Create() { 209 fbl::unique_ptr<DevicePartitioner> device_partitioner; 210#if defined(__x86_64__) 211 if ((CrosDevicePartitioner::Initialize(&device_partitioner) == ZX_OK) || 212 (EfiDevicePartitioner::Initialize(&device_partitioner) == ZX_OK)) { 213 return fbl::move(device_partitioner); 214 } 215#elif defined(__aarch64__) 216 if ((SkipBlockDevicePartitioner::Initialize(&device_partitioner) == ZX_OK) || 217 (FixedDevicePartitioner::Initialize(&device_partitioner) == ZX_OK)) { 218 return fbl::move(device_partitioner); 219 } 220#endif 221 return nullptr; 222} 223 224/*====================================================* 225 * GPT Common * 226 *====================================================*/ 227 228bool GptDevicePartitioner::FindTargetGptPath(fbl::String* out) { 229 DIR* d = opendir(kBlockDevPath); 230 if (d == nullptr) { 231 ERROR("Cannot inspect block devices\n"); 232 return false; 233 } 234 const auto closer = fbl::MakeAutoCall([&]() { closedir(d); }); 235 236 struct dirent* de; 237 while ((de = readdir(d)) != nullptr) { 238 fbl::unique_fd fd(openat(dirfd(d), de->d_name, O_RDWR)); 239 if (!fd) { 240 continue; 241 } 242 out->Set(PATH_MAX, '\0'); 243 ssize_t r = ioctl_device_get_topo_path(fd.get(), const_cast<char*>(out->data()), PATH_MAX); 244 if (r < 0) { 245 continue; 246 } 247 248 block_info_t info; 249 if ((r = ioctl_block_get_info(fd.get(), &info) < 0)) { 250 continue; 251 } 252 253 // TODO(ZX-1344): This is a hack, but practically, will work for our 254 // usage. 255 // 256 // The GPT which will contain an FVM should be the first non-removable 257 // block device that isn't a partition itself. 258 if (!(info.flags & BLOCK_FLAG_REMOVABLE) && strstr(out->c_str(), "part-") == nullptr) { 259 return true; 260 } 261 } 262 263 ERROR("No candidate GPT found\n"); 264 return false; 265} 266 267zx_status_t GptDevicePartitioner::InitializeGpt(fbl::unique_ptr<GptDevicePartitioner>* gpt_out) { 268 fbl::String gpt_path; 269 if (!FindTargetGptPath(&gpt_path)) { 270 ERROR("Failed to find GPT\n"); 271 return ZX_ERR_NOT_FOUND; 272 } 273 fbl::unique_fd fd(open(gpt_path.c_str(), O_RDWR)); 274 if (!fd) { 275 ERROR("Failed to open GPT\n"); 276 return ZX_ERR_NOT_FOUND; 277 } 278 block_info_t block_info; 279 ssize_t rc = ioctl_block_get_info(fd.get(), &block_info); 280 if (rc < 0) { 281 ERROR("Couldn't get GPT block info\n"); 282 return ZX_ERR_NOT_FOUND; 283 } 284 285 gpt_device_t* gpt; 286 if (gpt_device_init(fd.get(), block_info.block_size, block_info.block_count, &gpt)) { 287 ERROR("Failed to get GPT info\n"); 288 return ZX_ERR_BAD_STATE; 289 } 290 291 auto releaser = fbl::MakeAutoCall([&]() { gpt_device_release(gpt); }); 292 if (!gpt->valid) { 293 ERROR("Located GPT is invalid; Attempting to initialize\n"); 294 if (gpt_partition_remove_all(gpt)) { 295 ERROR("Failed to create empty GPT\n"); 296 return ZX_ERR_BAD_STATE; 297 } 298 if (gpt_device_sync(gpt)) { 299 ERROR("Failed to sync empty GPT\n"); 300 return ZX_ERR_BAD_STATE; 301 } 302 // Try to rebind the GPT, in case a prior GPT driver was actually 303 // up and running. 304 if ((rc = ioctl_block_rr_part(fd.get())) != ZX_OK) { 305 ERROR("Failed to re-read GPT\n"); 306 return ZX_ERR_BAD_STATE; 307 } 308 // Manually re-bind the GPT driver, since it is almost certainly 309 // too late to be noticed by the block watcher. 310 if ((rc = ioctl_device_bind(fd.get(), kGptDriverName, strlen(kGptDriverName))) != ZX_OK) { 311 ERROR("Failed to bind GPT\n"); 312 return ZX_ERR_BAD_STATE; 313 } 314 } 315 316 releaser.cancel(); 317 *gpt_out = fbl::move(WrapUnique(new GptDevicePartitioner(fbl::move(fd), gpt, block_info))); 318 return ZX_OK; 319} 320 321struct PartitionPosition { 322 size_t start; // Block, inclusive 323 size_t length; // In Blocks 324}; 325 326zx_status_t GptDevicePartitioner::FindFirstFit(size_t bytes_requested, size_t* start_out, 327 size_t* length_out) const { 328 LOG("Looking for space\n"); 329 // Gather GPT-related information. 330 size_t blocks_requested = 331 (bytes_requested + block_info_.block_size - 1) / block_info_.block_size; 332 333 // Sort all partitions by starting block. 334 // For simplicity, include the 'start' and 'end' reserved spots as 335 // partitions. 336 size_t partition_count = 0; 337 PartitionPosition partitions[PARTITIONS_COUNT + 2]; 338 const size_t reserved_blocks = ReservedHeaderBlocks(block_info_.block_size); 339 partitions[partition_count].start = 0; 340 partitions[partition_count++].length = reserved_blocks; 341 partitions[partition_count].start = block_info_.block_count - reserved_blocks; 342 partitions[partition_count++].length = reserved_blocks; 343 344 for (size_t i = 0; i < PARTITIONS_COUNT; i++) { 345 const gpt_partition_t* p = gpt_->partitions[i]; 346 if (!p) { 347 continue; 348 } 349 partitions[partition_count].start = p->first; 350 partitions[partition_count].length = p->last - p->first + 1; 351 LOG("Partition seen with start %zu, end %zu (length %zu)\n", p->first, p->last, 352 partitions[partition_count].length); 353 partition_count++; 354 } 355 LOG("Sorting\n"); 356 qsort(partitions, partition_count, sizeof(PartitionPosition), 357 [](const void* p1, const void* p2) { 358 ssize_t s1 = static_cast<ssize_t>(static_cast<const PartitionPosition*>(p1)->start); 359 ssize_t s2 = static_cast<ssize_t>(static_cast<const PartitionPosition*>(p2)->start); 360 return static_cast<int>(s1 - s2); 361 }); 362 363 // Look for space between the partitions. Since the reserved spots of the 364 // GPT were included in |partitions|, all available space will be located 365 // "between" partitions. 366 for (size_t i = 0; i < partition_count - 1; i++) { 367 const size_t next = partitions[i].start + partitions[i].length; 368 LOG("Partition[%zu] From Block [%zu, %zu) ... (next partition starts at block %zu)\n", 369 i, partitions[i].start, next, partitions[i + 1].start); 370 371 if (next > partitions[i + 1].start) { 372 ERROR("Corrupted GPT\n"); 373 return ZX_ERR_IO; 374 } 375 const size_t free_blocks = partitions[i + 1].start - next; 376 LOG(" There are %zu free blocks (%zu requested)\n", free_blocks, blocks_requested); 377 if (free_blocks >= blocks_requested) { 378 *start_out = next; 379 *length_out = free_blocks; 380 return ZX_OK; 381 } 382 } 383 ERROR("No GPT space found\n"); 384 return ZX_ERR_NO_RESOURCES; 385} 386 387zx_status_t GptDevicePartitioner::CreateGptPartition(const char* name, uint8_t* type, 388 uint64_t offset, uint64_t blocks, 389 uint8_t* out_guid) { 390 zx_cprng_draw(out_guid, GPT_GUID_LEN); 391 392 zx_status_t status; 393 if ((status = gpt_partition_add(gpt_, name, type, out_guid, offset, blocks, 0))) { 394 ERROR("Failed to add partition\n"); 395 return ZX_ERR_IO; 396 } 397 if ((status = gpt_device_sync(gpt_))) { 398 ERROR("Failed to sync GPT\n"); 399 return ZX_ERR_IO; 400 } 401 if ((status = gpt_partition_clear(gpt_, offset, 1))) { 402 ERROR("Failed to clear first block of new partition\n"); 403 return ZX_ERR_IO; 404 } 405 if ((status = static_cast<zx_status_t>(ioctl_block_rr_part(fd_.get()))) < 0) { 406 ERROR("Failed to rebind GPT\n"); 407 return status; 408 } 409 410 return ZX_OK; 411} 412 413zx_status_t GptDevicePartitioner::AddPartition( 414 const char* name, uint8_t* type, size_t minimum_size_bytes, 415 size_t optional_reserve_bytes, fbl::unique_fd* out_fd) { 416 417 uint64_t start, length; 418 zx_status_t status; 419 if ((status = FindFirstFit(minimum_size_bytes, &start, &length)) != ZX_OK) { 420 ERROR("Couldn't find fit\n"); 421 return status; 422 } 423 LOG("Found space in GPT - OK %zu @ %zu\n", length, start); 424 425 if (optional_reserve_bytes) { 426 // If we can fulfill the requested size, and we still have space for the 427 // optional reserve section, then we should shorten the amount of blocks 428 // we're asking for. 429 // 430 // This isn't necessary, but it allows growing the GPT later, if necessary. 431 const size_t optional_reserve_blocks = optional_reserve_bytes / block_info_.block_size; 432 if (length - optional_reserve_bytes > (minimum_size_bytes / block_info_.block_size)) { 433 LOG("Space for reserve - OK\n"); 434 length -= optional_reserve_blocks; 435 } 436 } else { 437 length = fbl::round_up(minimum_size_bytes, block_info_.block_size) / block_info_.block_size; 438 } 439 LOG("Final space in GPT - OK %zu @ %zu\n", length, start); 440 441 uint8_t guid[GPT_GUID_LEN]; 442 if ((status = CreateGptPartition(name, type, start, length, guid)) != ZX_OK) { 443 return status; 444 } 445 LOG("Added partition, waiting for bind\n"); 446 447 if ((status = OpenBlockPartition(guid, type, ZX_SEC(5), out_fd)) != ZX_OK) { 448 ERROR("Added partition, waiting for bind - NOT FOUND\n"); 449 return status; 450 } 451 LOG("Added partition, waiting for bind - OK\n"); 452 return ZX_OK; 453} 454 455zx_status_t GptDevicePartitioner::FindPartition(FilterCallback filter, gpt_partition_t** out, 456 fbl::unique_fd* out_fd) { 457 for (size_t i = 0; i < PARTITIONS_COUNT; i++) { 458 gpt_partition_t* p = gpt_->partitions[i]; 459 if (!p) { 460 continue; 461 } 462 463 if (filter(*p)) { 464 LOG("Found partition in GPT, partition %zu\n", i); 465 if (out) { 466 *out = p; 467 } 468 if (out_fd) { 469 zx_status_t status; 470 if ((status = OpenBlockPartition(p->guid, p->type, ZX_SEC(5), out_fd)) != ZX_OK) { 471 ERROR("Couldn't open partition\n"); 472 return status; 473 } 474 } 475 return ZX_OK; 476 } 477 } 478 return ZX_ERR_NOT_FOUND; 479} 480 481zx_status_t GptDevicePartitioner::FindPartition(FilterCallback filter, 482 fbl::unique_fd* out_fd) const { 483 for (size_t i = 0; i < PARTITIONS_COUNT; i++) { 484 const gpt_partition_t* p = gpt_->partitions[i]; 485 if (!p) { 486 continue; 487 } 488 489 if (filter(*p)) { 490 LOG("Found partition in GPT, partition %zu\n", i); 491 if (out_fd) { 492 zx_status_t status; 493 if ((status = OpenBlockPartition(p->guid, p->type, ZX_SEC(5), out_fd)) != ZX_OK) { 494 ERROR("Couldn't open partition\n"); 495 return status; 496 } 497 } 498 return ZX_OK; 499 } 500 } 501 return ZX_ERR_NOT_FOUND; 502} 503 504zx_status_t GptDevicePartitioner::WipePartitions(FilterCallback filter) { 505 bool modify = false; 506 for (size_t i = 0; i < PARTITIONS_COUNT; i++) { 507 const gpt_partition_t* p = gpt_->partitions[i]; 508 if (!p) { 509 continue; 510 } 511 if (!filter(*p)) { 512 continue; 513 } 514 515 modify = true; 516 517 // Ignore the return status; wiping is a best-effort approach anyway. 518 WipeBlockPartition(p->guid, p->type); 519 520 if (gpt_partition_remove(gpt_, p->guid)) { 521 ERROR("Warning: Could not remove partition\n"); 522 } else { 523 // If we successfully clear the partition, then all subsequent 524 // partitions get shifted down. If we just deleted partition 'i', 525 // we now need to look at partition 'i' again, since it's now 526 // occupied by what was in 'i+1'. 527 i--; 528 } 529 } 530 if (modify) { 531 gpt_device_sync(gpt_); 532 LOG("Immediate reboot strongly recommended\n"); 533 } 534 ioctl_block_rr_part(fd_.get()); 535 return ZX_OK; 536} 537 538/*====================================================* 539 * EFI SPECIFIC * 540 *====================================================*/ 541 542zx_status_t EfiDevicePartitioner::Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner) { 543 fbl::unique_ptr<GptDevicePartitioner> gpt; 544 zx_status_t status; 545 if ((status = GptDevicePartitioner::InitializeGpt(&gpt)) != ZX_OK) { 546 return status; 547 } 548 if (is_cros(gpt->GetGpt())) { 549 ERROR("Use CrOS Device Partitioner."); 550 return ZX_ERR_NOT_SUPPORTED; 551 } 552 553 LOG("Successfully intitialized EFI Device Partitioner\n"); 554 *partitioner = fbl::move(WrapUnique(new EfiDevicePartitioner(fbl::move(gpt)))); 555 return ZX_OK; 556} 557 558// Name used by previous Fuchsia Installer. 559constexpr char kOldEfiName[] = "EFI"; 560 561// Name used for EFI partitions added by paver. 562constexpr char kEfiName[] = "EFI Gigaboot"; 563 564zx_status_t EfiDevicePartitioner::AddPartition(Partition partition_type, fbl::unique_fd* out_fd) { 565 const char* name; 566 uint8_t type[GPT_GUID_LEN]; 567 size_t minimum_size_bytes = 0; 568 size_t optional_reserve_bytes = 0; 569 570 switch (partition_type) { 571 case Partition::kEfi: { 572 const uint8_t efi_type[GPT_GUID_LEN] = GUID_EFI_VALUE; 573 memcpy(type, efi_type, GPT_GUID_LEN); 574 minimum_size_bytes = 1LU * (1 << 30); 575 name = kEfiName; 576 break; 577 } 578 case Partition::kFuchsiaVolumeManager: { 579 const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 580 memcpy(type, fvm_type, GPT_GUID_LEN); 581 minimum_size_bytes = 8LU * (1 << 30); 582 name = kFvmPartitionName; 583 break; 584 } 585 default: 586 ERROR("EFI partitioner cannot add unknown partition type\n"); 587 return ZX_ERR_NOT_SUPPORTED; 588 } 589 590 return gpt_->AddPartition(name, type, minimum_size_bytes, 591 optional_reserve_bytes, out_fd); 592} 593 594bool EfiDevicePartitioner::FilterZirconPartition(const block_info_t& info, 595 const gpt_partition_t& part) { 596 const uint8_t efi_type[GPT_GUID_LEN] = GUID_EFI_VALUE; 597 char cstring_name[GPT_NAME_LEN]; 598 utf16_to_cstring(cstring_name, reinterpret_cast<const uint16_t*>(part.name), GPT_NAME_LEN); 599 // Old EFI: Installed by the legacy Fuchsia installer, identified by 600 // large size and "EFI" label. 601 constexpr unsigned int k512MB = (1LU << 29); 602 const bool old_efi = strncmp(cstring_name, kOldEfiName, strlen(kOldEfiName)) == 0 && 603 ((part.last - part.first + 1) * info.block_size) > k512MB; 604 // Disk-paved EFI: Identified by "EFI Gigaboot" label. 605 const bool new_efi = strncmp(cstring_name, kEfiName, strlen(kEfiName)) == 0; 606 return memcmp(part.type, efi_type, GPT_GUID_LEN) == 0 && (old_efi || new_efi); 607} 608 609zx_status_t EfiDevicePartitioner::FindPartition(Partition partition_type, 610 fbl::unique_fd* out_fd) const { 611 block_info_t info; 612 zx_status_t status; 613 if ((status = gpt_->GetBlockInfo(&info)) != ZX_OK) { 614 ERROR("Unable to get block info\n"); 615 return ZX_ERR_IO; 616 } 617 618 switch (partition_type) { 619 case Partition::kEfi: { 620 const auto filter = [&info](const gpt_partition_t& part) { 621 return FilterZirconPartition(info, part); 622 }; 623 return gpt_->FindPartition(filter, out_fd); 624 } 625 case Partition::kFuchsiaVolumeManager: 626 return gpt_->FindPartition(FvmFilterCallback, out_fd); 627 628 default: 629 ERROR("EFI partitioner cannot find unknown partition type\n"); 630 return ZX_ERR_NOT_SUPPORTED; 631 } 632} 633 634zx_status_t EfiDevicePartitioner::WipePartitions(const fbl::Vector<Partition>& partitions) { 635 const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 636 const uint8_t install_type[GPT_GUID_LEN] = GUID_INSTALL_VALUE; 637 const uint8_t system_type[GPT_GUID_LEN] = GUID_SYSTEM_VALUE; 638 const uint8_t blob_type[GPT_GUID_LEN] = GUID_BLOB_VALUE; 639 const uint8_t data_type[GPT_GUID_LEN] = GUID_DATA_VALUE; 640 641 block_info_t info; 642 zx_status_t status; 643 if ((status = gpt_->GetBlockInfo(&info)) != ZX_OK) { 644 ERROR("Unable to get block info\n"); 645 return ZX_ERR_IO; 646 } 647 648 fbl::Vector<const uint8_t*> partition_list; 649 bool efi = false; 650 for (const Partition& partition_type : partitions) { 651 switch (partition_type) { 652 case Partition::kEfi: { 653 // Special case. 654 efi = true; 655 break; 656 } 657 case Partition::kKernelC: 658 break; 659 case Partition::kFuchsiaVolumeManager: 660 partition_list.push_back(fvm_type); 661 break; 662 case Partition::kInstallType: 663 partition_list.push_back(install_type); 664 break; 665 case Partition::kSystem: 666 partition_list.push_back(system_type); 667 break; 668 case Partition::kBlob: 669 partition_list.push_back(blob_type); 670 break; 671 case Partition::kData: 672 partition_list.push_back(data_type); 673 break; 674 default: 675 return ZX_ERR_NOT_SUPPORTED; 676 } 677 } 678 679 // Early return if nothing to wipe. 680 if (partition_list.is_empty() && !efi) { 681 return ZX_OK; 682 } 683 684 const auto filter = [&info, &partition_list, efi](const gpt_partition_t& part) { 685 for (const auto& type : partition_list) { 686 if (memcmp(part.type, type, GPT_GUID_LEN) == 0) 687 return true; 688 } 689 if (efi) { 690 return FilterZirconPartition(info, part); 691 } 692 return false; 693 }; 694 return gpt_->WipePartitions(filter); 695} 696 697zx_status_t EfiDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd, 698 uint32_t* block_size) const { 699 block_info_t info; 700 zx_status_t status = gpt_->GetBlockInfo(&info); 701 if (status == ZX_OK) { 702 *block_size = info.block_size; 703 } 704 return status; 705} 706 707/*====================================================* 708 * CROS SPECIFIC * 709 *====================================================*/ 710 711zx_status_t CrosDevicePartitioner::Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner) { 712 fbl::unique_ptr<GptDevicePartitioner> gpt_partitioner; 713 zx_status_t status; 714 if ((status = GptDevicePartitioner::InitializeGpt(&gpt_partitioner)) != ZX_OK) { 715 return status; 716 } 717 718 gpt_device_t* gpt = gpt_partitioner->GetGpt(); 719 if (!is_cros(gpt)) { 720 return ZX_ERR_NOT_FOUND; 721 } 722 723 block_info_t info; 724 gpt_partitioner->GetBlockInfo(&info); 725 726 if (!is_ready_to_pave(gpt, &info, SZ_ZX_PART)) { 727 if ((status = config_cros_for_fuchsia(gpt, &info, SZ_ZX_PART)) != ZX_OK) { 728 ERROR("Failed to configure CrOS for Fuchsia.\n"); 729 return status; 730 } 731 gpt_device_sync(gpt); 732 ioctl_block_rr_part(gpt_partitioner->GetFd()); 733 } 734 735 LOG("Successfully initialized CrOS Device Partitioner\n"); 736 *partitioner = fbl::move(WrapUnique(new CrosDevicePartitioner(fbl::move(gpt_partitioner)))); 737 return ZX_OK; 738} 739 740constexpr char kZirconAName[] = "ZIRCON-A"; 741// TODO(raggi): near future - constexpr char kZirconBName[] = "ZIRCON-B"; 742// TODO(raggi): near future - constexpr char kZirconRName[] = "ZIRCON-R"; 743 744zx_status_t CrosDevicePartitioner::AddPartition(Partition partition_type, 745 fbl::unique_fd* out_fd) { 746 const char* name; 747 uint8_t type[GPT_GUID_LEN]; 748 size_t minimum_size_bytes = 0; 749 size_t optional_reserve_bytes = 0; 750 751 switch (partition_type) { 752 case Partition::kKernelC: { 753 const uint8_t kernc_type[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE; 754 memcpy(type, kernc_type, GPT_GUID_LEN); 755 minimum_size_bytes = 64LU * (1 << 20); 756 name = kZirconAName; 757 break; 758 } 759 case Partition::kFuchsiaVolumeManager: { 760 const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 761 memcpy(type, fvm_type, GPT_GUID_LEN); 762 minimum_size_bytes = 8LU * (1 << 30); 763 name = kFvmPartitionName; 764 break; 765 } 766 default: 767 ERROR("Cros partitioner cannot add unknown partition type\n"); 768 return ZX_ERR_NOT_SUPPORTED; 769 } 770 return gpt_->AddPartition(name, type, minimum_size_bytes, 771 optional_reserve_bytes, out_fd); 772} 773 774zx_status_t CrosDevicePartitioner::FindPartition(Partition partition_type, 775 fbl::unique_fd* out_fd) const { 776 switch (partition_type) { 777 case Partition::kKernelC: { 778 const auto filter = [](const gpt_partition_t& part) { 779 return KernelFilterCallback(part, kZirconAName); 780 }; 781 return gpt_->FindPartition(filter, out_fd); 782 } 783 case Partition::kFuchsiaVolumeManager: 784 return gpt_->FindPartition(FvmFilterCallback, out_fd); 785 786 default: 787 ERROR("Cros partitioner cannot find unknown partition type\n"); 788 return ZX_ERR_NOT_SUPPORTED; 789 } 790} 791 792zx_status_t CrosDevicePartitioner::FinalizePartition(Partition partition_type) { 793 // Special partition finalization is only necessary for Zircon partitions. 794 if (partition_type != Partition::kKernelC) { 795 return ZX_OK; 796 } 797 798 uint8_t top_priority = 0; 799 800 const uint8_t kern_type[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE; 801 constexpr char kPrefix[] = "ZIRCON-"; 802 uint16_t zircon_prefix[strlen(kPrefix)*2]; 803 cstring_to_utf16(&zircon_prefix[0], kPrefix, strlen(kPrefix)); 804 805 for(size_t i = 0; i < PARTITIONS_COUNT; ++i) { 806 const gpt_partition_t* part = gpt_->GetGpt()->partitions[i]; 807 if (part == NULL) { 808 continue; 809 } 810 if (memcmp(part->type, kern_type, GPT_GUID_LEN)) { 811 continue; 812 } 813 if (memcmp(part->name, zircon_prefix, strlen(kPrefix)*2)) { 814 const uint8_t priority = gpt_cros_attr_get_priority(part->flags); 815 if (priority > top_priority) { 816 top_priority = priority; 817 } 818 } 819 } 820 821 const auto filter_zircona = [](const gpt_partition_t& part) { 822 return KernelFilterCallback(part, kZirconAName); 823 }; 824 zx_status_t status; 825 gpt_partition_t* partition; 826 if ((status = gpt_->FindPartition(filter_zircona, &partition, nullptr)) != ZX_OK) { 827 ERROR("Cannot find %s partition\n", kZirconAName); 828 return status; 829 } 830 831 // Priority for Zircon A set to higher priority than all other kernels. 832 if (top_priority == UINT8_MAX) { 833 ERROR("Cannot set CrOS partition priority higher than other kernels\n"); 834 return ZX_ERR_OUT_OF_RANGE; 835 } 836 837 // TODO(raggi): when other (B/R) partitions are paved, set their priority 838 // appropriately as well. 839 840 if (gpt_cros_attr_set_priority(&partition->flags, ++top_priority) != 0) { 841 ERROR("Cannot set CrOS partition priority for ZIRCON-A\n"); 842 return ZX_ERR_OUT_OF_RANGE; 843 } 844 // Successful set to 'true' to encourage the bootloader to 845 // use this partition. 846 gpt_cros_attr_set_successful(&partition->flags, true); 847 // Maximize the number of attempts to boot this partition before 848 // we fall back to a different kernel. 849 if (gpt_cros_attr_set_tries(&partition->flags, 15) != 0) { 850 ERROR("Cannot set CrOS partition 'tries' for KERN-C\n"); 851 return ZX_ERR_OUT_OF_RANGE; 852 } 853 gpt_device_sync(gpt_->GetGpt()); 854 return ZX_OK; 855} 856 857zx_status_t CrosDevicePartitioner::WipePartitions(const fbl::Vector<Partition>& partitions) { 858 const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 859 const uint8_t install_type[GPT_GUID_LEN] = GUID_INSTALL_VALUE; 860 const uint8_t system_type[GPT_GUID_LEN] = GUID_SYSTEM_VALUE; 861 const uint8_t blob_type[GPT_GUID_LEN] = GUID_BLOB_VALUE; 862 const uint8_t data_type[GPT_GUID_LEN] = GUID_DATA_VALUE; 863 864 // TODO(raggi): add logic here to cleanup the kernc, rootc, and a/b/r partitions. 865 866 fbl::Vector<const uint8_t*> partition_list; 867 for (const auto& partition_type : partitions) { 868 switch (partition_type) { 869 case Partition::kEfi: 870 continue; 871 case Partition::kFuchsiaVolumeManager: 872 partition_list.push_back(fvm_type); 873 break; 874 case Partition::kInstallType: 875 partition_list.push_back(install_type); 876 break; 877 case Partition::kSystem: 878 partition_list.push_back(system_type); 879 break; 880 case Partition::kBlob: 881 partition_list.push_back(blob_type); 882 break; 883 case Partition::kData: 884 partition_list.push_back(data_type); 885 break; 886 default: 887 return ZX_ERR_NOT_SUPPORTED; 888 } 889 } 890 891 auto filter = [&](const gpt_partition_t& part) { 892 for (const auto& type : partition_list) { 893 if (memcmp(part.type, type, GPT_GUID_LEN) == 0) { 894 return true; 895 } 896 } 897 return false; 898 }; 899 return gpt_->WipePartitions(filter); 900} 901 902zx_status_t CrosDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd, 903 uint32_t* block_size) const { 904 block_info_t info; 905 zx_status_t status = gpt_->GetBlockInfo(&info); 906 if (status == ZX_OK) { 907 *block_size = info.block_size; 908 } 909 return status; 910} 911 912/*====================================================* 913 * FIXED PARTITION MAP * 914 *====================================================*/ 915 916zx_status_t FixedDevicePartitioner::Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner) { 917 if (HasSkipBlockDevice()) { 918 return ZX_ERR_NOT_SUPPORTED; 919 } 920 LOG("Successfully intitialized FixedDevicePartitioner Device Partitioner\n"); 921 *partitioner = fbl::move(WrapUnique(new FixedDevicePartitioner)); 922 return ZX_OK; 923} 924 925zx_status_t FixedDevicePartitioner::FindPartition(Partition partition_type, 926 fbl::unique_fd* out_fd) const { 927 uint8_t type[GPT_GUID_LEN]; 928 929 switch (partition_type) { 930 case Partition::kZirconA: { 931 const uint8_t zircon_a_type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE; 932 memcpy(type, zircon_a_type, GPT_GUID_LEN); 933 break; 934 } 935 case Partition::kZirconB: { 936 const uint8_t zircon_b_type[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE; 937 memcpy(type, zircon_b_type, GPT_GUID_LEN); 938 break; 939 } 940 case Partition::kZirconR: { 941 const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE; 942 memcpy(type, zircon_r_type, GPT_GUID_LEN); 943 break; 944 } 945 case Partition::kFuchsiaVolumeManager: { 946 const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 947 memcpy(type, fvm_type, GPT_GUID_LEN); 948 break; 949 } 950 default: 951 ERROR("partition_type is invalid!\n"); 952 return ZX_ERR_NOT_SUPPORTED; 953 } 954 955 return OpenBlockPartition(nullptr, type, ZX_SEC(5), out_fd); 956} 957 958zx_status_t FixedDevicePartitioner::WipePartitions(const fbl::Vector<Partition>& partitions) { 959 const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 960 zx_status_t status; 961 for (const Partition& partition_type : partitions) { 962 switch (partition_type) { 963 case Partition::kFuchsiaVolumeManager: 964 if ((status = WipeBlockPartition(nullptr, fvm_type)) != ZX_OK) { 965 ERROR("Failed to wipe FVM.\n"); 966 } else { 967 LOG("Wiped FVM successfully.\n"); 968 } 969 break; 970 default: 971 // All non-FVM partitions are currently ignored on FixedDevices. 972 continue; 973 } 974 } 975 LOG("Immediate reboot strongly recommended\n"); 976 return ZX_OK; 977} 978 979zx_status_t FixedDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd, 980 uint32_t* block_size) const { 981 ssize_t r; 982 block_info_t block_info; 983 if ((r = ioctl_block_get_info(device_fd.get(), &block_info) < 0)) { 984 return ZX_ERR_IO; 985 } 986 *block_size = block_info.block_size; 987 return ZX_OK; 988} 989 990/*====================================================* 991 * SKIP BLOCK SPECIFIC * 992 *====================================================*/ 993 994zx_status_t SkipBlockDevicePartitioner::Initialize( 995 fbl::unique_ptr<DevicePartitioner>* partitioner) { 996 997 if (!HasSkipBlockDevice()) { 998 return ZX_ERR_NOT_SUPPORTED; 999 } 1000 LOG("Successfully intitialized SkipBlockDevicePartitioner Device Partitioner\n"); 1001 *partitioner = fbl::move(WrapUnique(new SkipBlockDevicePartitioner)); 1002 return ZX_OK; 1003} 1004 1005zx_status_t SkipBlockDevicePartitioner::FindPartition(Partition partition_type, 1006 fbl::unique_fd* out_fd) const { 1007 uint8_t type[GPT_GUID_LEN]; 1008 1009 switch (partition_type) { 1010 case Partition::kBootloader: { 1011 const uint8_t bootloader_type[GPT_GUID_LEN] = GUID_BOOTLOADER_VALUE; 1012 memcpy(type, bootloader_type, GPT_GUID_LEN); 1013 break; 1014 } 1015 case Partition::kZirconA: { 1016 const uint8_t zircon_a_type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE; 1017 memcpy(type, zircon_a_type, GPT_GUID_LEN); 1018 break; 1019 } 1020 case Partition::kZirconB: { 1021 const uint8_t zircon_b_type[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE; 1022 memcpy(type, zircon_b_type, GPT_GUID_LEN); 1023 break; 1024 } 1025 case Partition::kZirconR: { 1026 const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE; 1027 memcpy(type, zircon_r_type, GPT_GUID_LEN); 1028 break; 1029 } 1030 case Partition::kFuchsiaVolumeManager: { 1031 const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 1032 memcpy(type, fvm_type, GPT_GUID_LEN); 1033 // FVM partition is managed so it should expose a normal block device. 1034 return OpenBlockPartition(nullptr, type, ZX_SEC(5), out_fd); 1035 } 1036 default: 1037 ERROR("partition_type is invalid!\n"); 1038 return ZX_ERR_NOT_SUPPORTED; 1039 } 1040 1041 return OpenSkipBlockPartition(type, ZX_SEC(5), out_fd); 1042} 1043 1044zx_status_t SkipBlockDevicePartitioner::WipePartitions(const fbl::Vector<Partition>& partitions) { 1045 const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; 1046 zx_status_t status; 1047 for (const Partition& partition_type : partitions) { 1048 switch (partition_type) { 1049 case Partition::kFuchsiaVolumeManager: 1050 if ((status = WipeBlockPartition(nullptr, fvm_type)) != ZX_OK) { 1051 ERROR("Failed to wipe FVM.\n"); 1052 } else { 1053 LOG("Wiped FVM successfully.\n"); 1054 } 1055 break; 1056 default: 1057 // All non-FVM partitions are currently ignored on SkipBlockDevices. 1058 continue; 1059 } 1060 } 1061 LOG("Immediate reboot strongly recommended\n"); 1062 return ZX_OK; 1063} 1064 1065zx_status_t SkipBlockDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd, 1066 uint32_t* block_size) const { 1067 // Just in case we are trying to get info about FVM. 1068 ssize_t r; 1069 block_info_t block_info; 1070 if ((r = ioctl_block_get_info(device_fd.get(), &block_info) >= 0)) { 1071 *block_size = block_info.block_size; 1072 return ZX_OK; 1073 } 1074 1075 skip_block_partition_info_t info; 1076 if ((r = ioctl_skip_block_get_partition_info(device_fd.get(), &info) < 0)) { 1077 return ZX_ERR_IO; 1078 } 1079 *block_size = static_cast<uint32_t>(info.block_size_bytes); 1080 1081 return ZX_OK; 1082} 1083 1084} // namespace paver 1085