2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
3 * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de.
5 * Distributed under the terms of the MIT License.
11 #include <KernelExport.h>
12 #include <disk_device_manager/ddm_modules.h>
13 #include <disk_device_types.h>
15 # include <boot/partitions.h>
17 # include <DiskDeviceTypes.h>
18 # include "PartitionLocker.h"
20 #include <util/kernel_cpp.h>
32 # define TRACE(x) dprintf x
38 #define EFI_PARTITION_MODULE_NAME "partitioning_systems/efi_gpt/v1"
43 block_align(partition_data
* partition
, off_t offset
, bool upwards
)
45 // Take HDs into account that hide the fact they are using a
46 // block size of 4096 bytes, and round to that.
47 uint32 blockSize
= max_c(partition
->block_size
, 4096);
49 return ((offset
+ blockSize
- 1) / blockSize
) * blockSize
;
51 return (offset
/ blockSize
) * blockSize
;
56 // #pragma mark - public module interface
60 efi_gpt_std_ops(int32 op
, ...)
73 efi_gpt_identify_partition(int fd
, partition_data
* partition
, void** _cookie
)
75 EFI::Header
* header
= new (std::nothrow
) EFI::Header(fd
,
76 (partition
->size
- 1) / partition
->block_size
, partition
->block_size
);
77 status_t status
= header
->InitCheck();
85 // This must be higher as Intel partitioning, as EFI can contain this
86 // partitioning for compatibility
91 efi_gpt_scan_partition(int fd
, partition_data
* partition
, void* _cookie
)
93 TRACE(("efi_gpt_scan_partition(cookie = %p)\n", _cookie
));
94 EFI::Header
* header
= (EFI::Header
*)_cookie
;
96 partition
->status
= B_PARTITION_VALID
;
97 partition
->flags
|= B_PARTITION_PARTITIONING_SYSTEM
;
98 partition
->content_size
= partition
->size
;
99 partition
->content_cookie
= header
;
105 for (uint32 i
= 0; i
< header
->EntryCount(); i
++) {
106 const efi_partition_entry
& entry
= header
->EntryAt(i
);
108 if (entry
.partition_type
== kEmptyGUID
)
111 if (entry
.EndBlock() * partition
->block_size
112 > (uint64
)partition
->size
) {
113 TRACE(("efi_gpt: child partition exceeds existing space (ends at "
114 "block %" B_PRIu64
")\n", entry
.EndBlock()));
118 if (entry
.StartBlock() * partition
->block_size
== 0) {
119 TRACE(("efi_gpt: child partition starts at 0 (recursive entry)\n"));
123 partition_data
* child
= create_child_partition(partition
->id
, index
++,
124 partition
->offset
+ entry
.StartBlock() * partition
->block_size
,
125 entry
.BlockCount() * partition
->block_size
, -1);
127 TRACE(("efi_gpt: Creating child at index %" B_PRIu32
" failed\n",
132 char name
[B_OS_NAME_LENGTH
];
133 to_utf8(entry
.name
, EFI_PARTITION_NAME_LENGTH
, name
, sizeof(name
));
134 child
->name
= strdup(name
);
135 child
->type
= strdup(get_partition_type(entry
.partition_type
));
136 child
->block_size
= partition
->block_size
;
137 child
->cookie
= (void*)(addr_t
)i
;
145 efi_gpt_free_identify_partition_cookie(partition_data
* partition
, void* _cookie
)
147 // Cookie is freed in efi_gpt_free_partition_content_cookie().
152 efi_gpt_free_partition_content_cookie(partition_data
* partition
)
154 delete (EFI::Header
*)partition
->content_cookie
;
160 efi_gpt_get_supported_operations(partition_data
* partition
, uint32 mask
)
162 uint32 flags
= B_DISK_SYSTEM_SUPPORTS_INITIALIZING
163 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
164 | B_DISK_SYSTEM_SUPPORTS_MOVING
165 | B_DISK_SYSTEM_SUPPORTS_RESIZING
166 | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
;
167 // TODO: check for available entries and partitionable space and only
168 // add creating child support if both is valid
175 efi_gpt_get_supported_child_operations(partition_data
* partition
,
176 partition_data
* child
, uint32 mask
)
178 return B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
179 | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
180 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
181 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
;
186 efi_gpt_is_sub_system_for(partition_data
* partition
)
188 // a GUID Partition Table doesn't usually live inside another partition
194 efi_gpt_validate_resize(partition_data
* partition
, off_t
* size
)
196 off_t newSize
= *size
;
197 if (newSize
== partition
->size
)
203 newSize
= block_align(partition
, newSize
, false);
206 if (newSize
> partition
->size
) {
211 // shrinking, only so that no child would be truncated
212 off_t newEnd
= partition
->offset
+ newSize
;
213 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
214 partition_data
* child
= get_child_partition(partition
->id
, i
);
218 if (child
->offset
+ child
->size
> newEnd
)
219 newEnd
= child
->offset
+ child
->size
;
222 newSize
= block_align(partition
, newEnd
- partition
->offset
, true);
229 efi_gpt_validate_resize_child(partition_data
* partition
, partition_data
* child
,
232 off_t newSize
= *size
;
233 if (newSize
== child
->size
)
237 if (newSize
< child
->size
) {
241 *size
= block_align(partition
, newSize
, false);
245 // growing, but only so much that the child doesn't get bigger than
247 if (child
->offset
+ newSize
> partition
->offset
+ partition
->size
)
248 newSize
= partition
->offset
+ partition
->size
- child
->offset
;
250 // make sure that the child doesn't overlap any sibling partitions
251 off_t newEnd
= child
->offset
+ newSize
;
252 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
253 partition_data
* other
= get_child_partition(partition
->id
, i
);
254 if (other
== NULL
|| other
->id
== child
->id
255 || other
->offset
< child
->offset
)
258 if (newEnd
> other
->offset
)
259 newEnd
= other
->offset
;
262 *size
= block_align(partition
, newEnd
- child
->offset
, false);
268 efi_gpt_validate_move(partition_data
* partition
, off_t
* start
)
276 efi_gpt_validate_move_child(partition_data
* partition
, partition_data
* child
,
279 off_t newStart
= *start
;
283 if (newStart
+ child
->size
> partition
->size
)
284 newStart
= partition
->size
- child
->size
;
286 newStart
= block_align(partition
, newStart
, false);
287 if (newStart
> child
->offset
) {
288 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
289 partition_data
* other
= get_child_partition(partition
->id
, i
);
290 if (other
== NULL
|| other
->id
== child
->id
291 || other
->offset
< child
->offset
)
294 if (other
->offset
< newStart
+ child
->size
)
295 newStart
= other
->offset
- child
->size
;
298 newStart
= block_align(partition
, newStart
, false);
300 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
301 partition_data
* other
= get_child_partition(partition
->id
, i
);
302 if (other
== NULL
|| other
->id
== child
->id
303 || other
->offset
> child
->offset
)
306 if (other
->offset
+ other
->size
> newStart
)
307 newStart
= other
->offset
+ other
->size
;
310 newStart
= block_align(partition
, newStart
, true);
319 efi_gpt_validate_set_content_name(partition_data
* partition
, char* name
)
321 // TODO: should validate that the utf-8 -> ucs-2 is valid
322 // TODO: should count actual utf-8 chars
323 if (strlen(name
) > EFI_PARTITION_NAME_LENGTH
)
324 name
[EFI_PARTITION_NAME_LENGTH
- 1] = 0;
330 efi_gpt_validate_set_type(partition_data
* partition
, const char* type
)
333 return get_guid_for_partition_type(type
, typeGUID
);
338 efi_gpt_validate_initialize(partition_data
* partition
, char* name
,
339 const char* parameters
)
341 if ((efi_gpt_get_supported_operations(partition
, ~0)
342 & B_DISK_SYSTEM_SUPPORTS_INITIALIZING
) == 0)
345 // name and parameters are ignored
354 efi_gpt_validate_create_child(partition_data
* partition
, off_t
* start
,
355 off_t
* size
, const char* type
, const char* name
, const char* parameters
,
358 if ((efi_gpt_get_supported_operations(partition
, ~0)
359 & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
) == 0)
362 if (!efi_gpt_validate_set_type(partition
, type
))
365 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
366 int32 entryIndex
= -1;
367 for (uint32 i
= 0; i
< header
->EntryCount(); i
++) {
368 const efi_partition_entry
& entry
= header
->EntryAt(i
);
369 if (entry
.partition_type
== kEmptyGUID
) {
380 // ensure that child lies between first and last usable block
381 off_t firstUsable
= header
->FirstUsableBlock() * partition
->block_size
;
382 if (*start
< firstUsable
)
383 *start
= firstUsable
;
385 off_t lastUsable
= header
->LastUsableBlock() * partition
->block_size
;
386 if (*start
+ *size
> lastUsable
) {
387 if (*start
> lastUsable
)
390 *size
= lastUsable
- *start
;
393 // ensure that we don't overlap any siblings
394 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
395 partition_data
* other
= get_child_partition(partition
->id
, i
);
399 if (other
->offset
< *start
&& other
->offset
+ other
->size
> *start
)
400 *start
= other
->offset
+ other
->size
;
402 if (other
->offset
> *start
&& other
->offset
< *start
+ *size
)
403 *size
= other
->offset
- *start
;
406 *start
= block_align(partition
, *start
, true);
407 *size
= block_align(partition
, *size
, false);
409 // TODO: support parameters
415 efi_gpt_get_partitionable_spaces(partition_data
* partition
,
416 partitionable_space_data
* buffer
, int32 count
, int32
* actualCount
)
424 efi_gpt_get_next_supported_type(partition_data
* partition
, int32
* cookie
,
433 efi_gpt_shadow_changed(partition_data
* partition
, partition_data
* child
,
442 efi_gpt_repair(int fd
, partition_id partition
, bool checkOnly
, disk_job_id job
)
444 // TODO: implement, validate CRCs and restore from backup area if corrupt
450 efi_gpt_resize(int fd
, partition_id partitionID
, off_t size
, disk_job_id job
)
455 PartitionWriteLocker
locker(partitionID
);
456 if (!locker
.IsLocked())
459 partition_data
* partition
= get_partition(partitionID
);
460 if (partition
== NULL
)
463 off_t validatedSize
= size
;
464 if (!efi_gpt_validate_resize(partition
, &validatedSize
))
467 update_disk_device_job_progress(job
, 0.0);
469 partition
->size
= validatedSize
;
470 partition
->content_size
= validatedSize
;
472 update_disk_device_job_progress(job
, 1.0);
473 partition_modified(partitionID
);
479 efi_gpt_resize_child(int fd
, partition_id partitionID
, off_t size
,
485 PartitionWriteLocker
locker(partitionID
);
486 if (!locker
.IsLocked())
489 partition_data
* child
= get_partition(partitionID
);
493 partition_data
* partition
= get_parent_partition(partitionID
);
494 if (partition
== NULL
)
497 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
501 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
502 if (entryIndex
>= header
->EntryCount())
505 off_t validatedSize
= size
;
506 if (!efi_gpt_validate_resize_child(partition
, child
, &validatedSize
))
509 if (child
->size
== validatedSize
)
512 update_disk_device_job_progress(job
, 0.0);
514 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
515 entry
.SetBlockCount(validatedSize
/ partition
->block_size
);
517 status_t result
= header
->WriteEntry(fd
, entryIndex
);
518 if (result
!= B_OK
) {
519 entry
.SetBlockCount(child
->size
/ partition
->block_size
);
523 child
->size
= validatedSize
;
525 update_disk_device_job_progress(job
, 1.0);
526 partition_modified(partitionID
);
532 efi_gpt_move(int fd
, partition_id partition
, off_t offset
, disk_job_id job
)
534 // nothing to do here
540 efi_gpt_move_child(int fd
, partition_id partitionID
, partition_id childID
,
541 off_t offset
, disk_job_id job
)
546 PartitionWriteLocker
locker(partitionID
);
547 if (!locker
.IsLocked())
550 partition_data
* partition
= get_partition(partitionID
);
551 if (partition
== NULL
)
554 partition_data
* child
= get_partition(childID
);
558 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
562 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
563 if (entryIndex
>= header
->EntryCount())
566 off_t validatedOffset
= offset
;
567 if (!efi_gpt_validate_move_child(partition
, child
, &validatedOffset
))
570 if (child
->offset
== validatedOffset
)
573 // TODO: implement actual moving, need to move the partition content
574 // (the raw data) here and need to take overlap into account
577 update_disk_device_job_progress(job
, 0.0);
579 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
580 uint64 blockCount
= entry
.BlockCount();
581 entry
.SetStartBlock((validatedOffset
- partition
->offset
)
582 / partition
->block_size
);
583 entry
.SetBlockCount(blockCount
);
585 status_t result
= header
->WriteEntry(fd
, entryIndex
);
586 if (result
!= B_OK
) {
587 // fatal error: the data has been moved but the partition table could
588 // not be updated to reflect that change!
592 child
->offset
= validatedOffset
;
594 update_disk_device_job_progress(job
, 1.0);
595 partition_modified(childID
);
601 efi_gpt_set_content_name(int fd
, partition_id partitionID
, const char* name
,
607 PartitionWriteLocker
locker(partitionID
);
608 if (!locker
.IsLocked())
611 partition_data
* child
= get_partition(partitionID
);
615 partition_data
* partition
= get_parent_partition(partitionID
);
616 if (partition
== NULL
)
619 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
623 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
624 if (entryIndex
>= header
->EntryCount())
627 update_disk_device_job_progress(job
, 0.0);
629 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
630 to_ucs2(name
, strlen(name
), entry
.name
, EFI_PARTITION_NAME_LENGTH
);
632 status_t result
= header
->WriteEntry(fd
, entryIndex
);
636 char newName
[B_OS_NAME_LENGTH
];
637 to_utf8(entry
.name
, EFI_PARTITION_NAME_LENGTH
, newName
, sizeof(newName
));
638 child
->name
= strdup(newName
);
640 update_disk_device_job_progress(job
, 1.0);
641 partition_modified(partitionID
);
647 efi_gpt_set_type(int fd
, partition_id partitionID
, const char* type
,
653 PartitionWriteLocker
locker(partitionID
);
654 if (!locker
.IsLocked())
657 partition_data
* child
= get_partition(partitionID
);
661 partition_data
* partition
= get_parent_partition(partitionID
);
662 if (partition
== NULL
)
665 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
669 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
670 if (entryIndex
>= header
->EntryCount())
674 if (!get_guid_for_partition_type(type
, typeGUID
))
677 update_disk_device_job_progress(job
, 0.0);
679 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
680 entry
.partition_type
= typeGUID
;
682 status_t result
= header
->WriteEntry(fd
, entryIndex
);
686 child
->type
= strdup(type
);
688 update_disk_device_job_progress(job
, 1.0);
689 partition_modified(partitionID
);
695 efi_gpt_initialize(int fd
, partition_id partitionID
, const char* name
,
696 const char* parameters
, off_t partitionSize
, disk_job_id job
)
701 partition_data
* partition
= get_partition(partitionID
);
702 if (partition
== NULL
)
705 update_disk_device_job_progress(job
, 0.0);
707 EFI::Header
header((partitionSize
- 1) / partition
->block_size
,
708 partition
->block_size
);
709 status_t result
= header
.InitCheck();
713 result
= header
.Write(fd
);
717 result
= scan_partition(partitionID
);
721 update_disk_device_job_progress(job
, 1.0);
722 partition_modified(partitionID
);
728 efi_gpt_create_child(int fd
, partition_id partitionID
, off_t offset
,
729 off_t size
, const char* type
, const char* name
, const char* parameters
,
730 disk_job_id job
, partition_id
* childID
)
735 PartitionWriteLocker
locker(partitionID
);
736 if (!locker
.IsLocked())
739 partition_data
* partition
= get_partition(partitionID
);
740 if (partition
== NULL
)
743 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
747 off_t validatedOffset
= offset
;
748 off_t validatedSize
= size
;
749 uint32 entryIndex
= 0;
751 if (!efi_gpt_validate_create_child(partition
, &validatedOffset
,
752 &validatedSize
, type
, name
, parameters
, (int32
*)&entryIndex
))
756 if (!get_guid_for_partition_type(type
, typeGUID
))
759 update_disk_device_job_progress(job
, 0.0);
761 partition_data
* child
= create_child_partition(partition
->id
, entryIndex
,
762 validatedOffset
, validatedSize
, *childID
);
766 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
767 entry
.partition_type
= typeGUID
;
768 // TODO: set unique partition ID
769 to_ucs2(name
, strlen(name
), entry
.name
, EFI_PARTITION_NAME_LENGTH
);
770 entry
.SetStartBlock((validatedOffset
- partition
->offset
)
771 / partition
->block_size
);
772 entry
.SetBlockCount(validatedSize
/ partition
->block_size
);
773 entry
.SetAttributes(0); // TODO
775 status_t result
= header
->WriteEntry(fd
, entryIndex
);
776 if (result
!= B_OK
) {
777 delete_partition(child
->id
);
781 *childID
= child
->id
;
782 child
->block_size
= partition
->block_size
;
783 child
->name
= strdup(name
);
784 child
->type
= strdup(type
);
785 child
->parameters
= strdup(parameters
);
786 child
->cookie
= (void*)(addr_t
)entryIndex
;
788 if (child
->type
== NULL
|| child
->parameters
== NULL
) {
789 delete_partition(child
->id
);
793 update_disk_device_job_progress(job
, 1.0);
794 partition_modified(partitionID
);
800 efi_gpt_delete_child(int fd
, partition_id partitionID
, partition_id childID
,
806 PartitionWriteLocker
locker(partitionID
);
807 if (!locker
.IsLocked())
810 partition_data
* partition
= get_partition(partitionID
);
811 if (partition
== NULL
)
814 partition_data
* child
= get_partition(childID
);
818 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
822 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
823 if (entryIndex
>= header
->EntryCount())
826 update_disk_device_job_progress(job
, 0.0);
828 if (!delete_partition(childID
))
831 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
832 entry
.partition_type
= kEmptyGUID
;
834 status_t result
= header
->WriteEntry(fd
, entryIndex
);
838 update_disk_device_job_progress(job
, 1.0);
839 partition_modified(partitionID
);
842 #endif // !_BOOT_MODE
846 static partition_module_info sEFIPartitionModule
= {
848 partition_module_info gEFIPartitionModule
= {
851 EFI_PARTITION_MODULE_NAME
,
856 EFI_PARTITION_NAME
, // pretty_name
858 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING
859 | B_DISK_SYSTEM_SUPPORTS_MOVING
860 | B_DISK_SYSTEM_SUPPORTS_RESIZING
861 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
862 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
863 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
864 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
865 | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
866 | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
867 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
871 efi_gpt_identify_partition
,
872 efi_gpt_scan_partition
,
873 efi_gpt_free_identify_partition_cookie
,
874 NULL
, // free_partition_cookie
875 efi_gpt_free_partition_content_cookie
,
879 efi_gpt_get_supported_operations
,
880 efi_gpt_get_supported_child_operations
,
881 NULL
, // supports_initializing_child
882 efi_gpt_is_sub_system_for
,
884 efi_gpt_validate_resize
,
885 efi_gpt_validate_resize_child
,
886 efi_gpt_validate_move
,
887 efi_gpt_validate_move_child
,
888 NULL
, // validate_set_name
889 efi_gpt_validate_set_content_name
,
890 efi_gpt_validate_set_type
,
891 NULL
, // validate_set_parameters
892 NULL
, // validate_set_content_parameters
893 efi_gpt_validate_initialize
,
894 efi_gpt_validate_create_child
,
895 efi_gpt_get_partitionable_spaces
,
896 efi_gpt_get_next_supported_type
,
897 NULL
, // get_type_for_content_type
899 // shadow partition modification
900 efi_gpt_shadow_changed
,
905 efi_gpt_resize_child
,
909 efi_gpt_set_content_name
,
911 NULL
, // set_parameters
912 NULL
, // set_content_parameters
914 NULL
, // uninitialize
915 efi_gpt_create_child
,
923 partition_module_info
* modules
[] = {
924 &sEFIPartitionModule
,