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