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>
37 # define TRACE(x) dprintf x
43 #define EFI_PARTITION_MODULE_NAME "partitioning_systems/efi_gpt/v1"
48 block_align(partition_data
* partition
, off_t offset
, bool upwards
)
50 // Take HDs into account that hide the fact they are using a
51 // block size of 4096 bytes, and round to that.
52 uint32 blockSize
= max_c(partition
->block_size
, 4096);
54 return ((offset
+ blockSize
- 1) / blockSize
) * blockSize
;
56 return (offset
/ blockSize
) * blockSize
;
61 // #pragma mark - public module interface
65 efi_gpt_std_ops(int32 op
, ...)
78 efi_gpt_identify_partition(int fd
, partition_data
* partition
, void** _cookie
)
80 EFI::Header
* header
= new (std::nothrow
) EFI::Header(fd
,
81 (partition
->size
- 1) / partition
->block_size
, partition
->block_size
);
82 status_t status
= header
->InitCheck();
89 if (header
->IsDirty()) {
90 // Either the main or the backup table is missing, it looks like someone
91 // tried to erase the GPT with something else. Let's lower the priority,
92 // so that other partitioning systems which use either only the start or
93 // only the end of the drive, have a chance to run instead.
97 // This must be higher as Intel partitioning, as EFI can contain this
98 // partitioning for compatibility
103 efi_gpt_scan_partition(int fd
, partition_data
* partition
, void* _cookie
)
105 TRACE(("efi_gpt_scan_partition(cookie = %p)\n", _cookie
));
106 EFI::Header
* header
= (EFI::Header
*)_cookie
;
108 partition
->status
= B_PARTITION_VALID
;
109 partition
->flags
|= B_PARTITION_PARTITIONING_SYSTEM
;
110 partition
->content_size
= partition
->size
;
111 partition
->content_cookie
= header
;
117 for (uint32 i
= 0; i
< header
->EntryCount(); i
++) {
118 const efi_partition_entry
& entry
= header
->EntryAt(i
);
120 if (entry
.partition_type
== kEmptyGUID
)
123 if (entry
.EndBlock() * partition
->block_size
124 > (uint64
)partition
->size
) {
125 TRACE(("efi_gpt: child partition exceeds existing space (ends at "
126 "block %" B_PRIu64
")\n", entry
.EndBlock()));
130 if (entry
.StartBlock() * partition
->block_size
== 0) {
131 TRACE(("efi_gpt: child partition starts at 0 (recursive entry)\n"));
135 partition_data
* child
= create_child_partition(partition
->id
, index
++,
136 partition
->offset
+ entry
.StartBlock() * partition
->block_size
,
137 entry
.BlockCount() * partition
->block_size
, -1);
139 TRACE(("efi_gpt: Creating child at index %" B_PRIu32
" failed\n",
144 char name
[B_OS_NAME_LENGTH
];
145 to_utf8(entry
.name
, EFI_PARTITION_NAME_LENGTH
, name
, sizeof(name
));
146 child
->name
= strdup(name
);
147 child
->type
= strdup(get_partition_type(entry
.partition_type
));
148 child
->block_size
= partition
->block_size
;
149 child
->cookie
= (void*)(addr_t
)i
;
150 child
->content_cookie
= header
;
158 efi_gpt_free_identify_partition_cookie(partition_data
* partition
, void* _cookie
)
160 // Cookie is freed in efi_gpt_free_partition_content_cookie().
165 efi_gpt_free_partition_content_cookie(partition_data
* partition
)
167 delete (EFI::Header
*)partition
->content_cookie
;
173 efi_gpt_get_supported_operations(partition_data
* partition
, uint32 mask
)
175 uint32 flags
= B_DISK_SYSTEM_SUPPORTS_INITIALIZING
176 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
177 | B_DISK_SYSTEM_SUPPORTS_MOVING
178 | B_DISK_SYSTEM_SUPPORTS_RESIZING
179 | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
;
180 // TODO: check for available entries and partitionable space and only
181 // add creating child support if both is valid
188 efi_gpt_get_supported_child_operations(partition_data
* partition
,
189 partition_data
* child
, uint32 mask
)
191 return B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
192 | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
193 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
194 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
;
199 efi_gpt_is_sub_system_for(partition_data
* partition
)
201 // a GUID Partition Table doesn't usually live inside another partition
207 efi_gpt_validate_resize(partition_data
* partition
, off_t
* size
)
209 off_t newSize
= *size
;
210 if (newSize
== partition
->size
)
216 newSize
= block_align(partition
, newSize
, false);
219 if (newSize
> partition
->size
) {
224 // shrinking, only so that no child would be truncated
225 off_t newEnd
= partition
->offset
+ newSize
;
226 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
227 partition_data
* child
= get_child_partition(partition
->id
, i
);
231 if (child
->offset
+ child
->size
> newEnd
)
232 newEnd
= child
->offset
+ child
->size
;
235 newSize
= block_align(partition
, newEnd
- partition
->offset
, true);
242 efi_gpt_validate_resize_child(partition_data
* partition
, partition_data
* child
,
245 off_t newSize
= *size
;
246 if (newSize
== child
->size
)
250 if (newSize
< child
->size
) {
254 *size
= block_align(partition
, newSize
, false);
258 // growing, but only so much that the child doesn't get bigger than
260 if (child
->offset
+ newSize
> partition
->offset
+ partition
->size
)
261 newSize
= partition
->offset
+ partition
->size
- child
->offset
;
263 // make sure that the child doesn't overlap any sibling partitions
264 off_t newEnd
= child
->offset
+ newSize
;
265 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
266 partition_data
* other
= get_child_partition(partition
->id
, i
);
267 if (other
== NULL
|| other
->id
== child
->id
268 || other
->offset
< child
->offset
)
271 if (newEnd
> other
->offset
)
272 newEnd
= other
->offset
;
275 *size
= block_align(partition
, newEnd
- child
->offset
, false);
281 efi_gpt_validate_move(partition_data
* partition
, off_t
* start
)
289 efi_gpt_validate_move_child(partition_data
* partition
, partition_data
* child
,
292 off_t newStart
= *start
;
296 if (newStart
+ child
->size
> partition
->size
)
297 newStart
= partition
->size
- child
->size
;
299 newStart
= block_align(partition
, newStart
, false);
300 if (newStart
> child
->offset
) {
301 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
302 partition_data
* other
= get_child_partition(partition
->id
, i
);
303 if (other
== NULL
|| other
->id
== child
->id
304 || other
->offset
< child
->offset
)
307 if (other
->offset
< newStart
+ child
->size
)
308 newStart
= other
->offset
- child
->size
;
311 newStart
= block_align(partition
, newStart
, false);
313 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
314 partition_data
* other
= get_child_partition(partition
->id
, i
);
315 if (other
== NULL
|| other
->id
== child
->id
316 || other
->offset
> child
->offset
)
319 if (other
->offset
+ other
->size
> newStart
)
320 newStart
= other
->offset
+ other
->size
;
323 newStart
= block_align(partition
, newStart
, true);
332 efi_gpt_validate_set_name(partition_data
* partition
, char* name
)
334 // TODO: should validate that the utf-8 -> ucs-2 is valid
335 // TODO: should count actual utf-8 chars
336 if (strlen(name
) > EFI_PARTITION_NAME_LENGTH
)
337 name
[EFI_PARTITION_NAME_LENGTH
- 1] = 0;
343 efi_gpt_validate_set_type(partition_data
* partition
, const char* type
)
346 return get_guid_for_partition_type(type
, typeGUID
);
351 efi_gpt_validate_initialize(partition_data
* partition
, char* name
,
352 const char* parameters
)
354 if ((efi_gpt_get_supported_operations(partition
, ~0)
355 & B_DISK_SYSTEM_SUPPORTS_INITIALIZING
) == 0)
358 // name and parameters are ignored
367 efi_gpt_validate_create_child(partition_data
* partition
, off_t
* start
,
368 off_t
* size
, const char* type
, const char* name
, const char* parameters
,
371 if ((efi_gpt_get_supported_operations(partition
, ~0)
372 & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
) == 0)
375 if (!efi_gpt_validate_set_type(partition
, type
))
378 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
379 int32 entryIndex
= -1;
380 for (uint32 i
= 0; i
< header
->EntryCount(); i
++) {
381 const efi_partition_entry
& entry
= header
->EntryAt(i
);
382 if (entry
.partition_type
== kEmptyGUID
) {
393 // ensure that child lies between first and last usable block
394 off_t firstUsable
= header
->FirstUsableBlock() * partition
->block_size
;
395 if (*start
< firstUsable
)
396 *start
= firstUsable
;
398 off_t lastUsable
= header
->LastUsableBlock() * partition
->block_size
;
399 if (*start
+ *size
> lastUsable
) {
400 if (*start
> lastUsable
)
403 *size
= lastUsable
- *start
;
406 // ensure that we don't overlap any siblings
407 for (int32 i
= 0; i
< partition
->child_count
; i
++) {
408 partition_data
* other
= get_child_partition(partition
->id
, i
);
412 if (other
->offset
< *start
&& other
->offset
+ other
->size
> *start
)
413 *start
= other
->offset
+ other
->size
;
415 if (other
->offset
> *start
&& other
->offset
< *start
+ *size
)
416 *size
= other
->offset
- *start
;
419 *start
= block_align(partition
, *start
, true);
420 *size
= block_align(partition
, *size
, false);
422 // TODO: support parameters
428 efi_gpt_get_partitionable_spaces(partition_data
* partition
,
429 partitionable_space_data
* buffer
, int32 count
, int32
* actualCount
)
437 efi_gpt_get_next_supported_type(partition_data
* partition
, int32
* cookie
,
446 efi_gpt_shadow_changed(partition_data
* partition
, partition_data
* child
,
455 efi_gpt_repair(int fd
, partition_id partition
, bool checkOnly
, disk_job_id job
)
457 // TODO: implement, validate CRCs and restore from backup area if corrupt
463 efi_gpt_resize(int fd
, partition_id partitionID
, off_t size
, disk_job_id job
)
468 PartitionWriteLocker
locker(partitionID
);
469 if (!locker
.IsLocked())
472 partition_data
* partition
= get_partition(partitionID
);
473 if (partition
== NULL
)
476 off_t validatedSize
= size
;
477 if (!efi_gpt_validate_resize(partition
, &validatedSize
))
480 update_disk_device_job_progress(job
, 0.0);
482 partition
->size
= validatedSize
;
483 partition
->content_size
= validatedSize
;
485 update_disk_device_job_progress(job
, 1.0);
486 partition_modified(partitionID
);
492 efi_gpt_resize_child(int fd
, partition_id partitionID
, off_t size
,
498 PartitionWriteLocker
locker(partitionID
);
499 if (!locker
.IsLocked())
502 partition_data
* child
= get_partition(partitionID
);
506 partition_data
* partition
= get_parent_partition(partitionID
);
507 if (partition
== NULL
)
510 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
514 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
515 if (entryIndex
>= header
->EntryCount())
518 off_t validatedSize
= size
;
519 if (!efi_gpt_validate_resize_child(partition
, child
, &validatedSize
))
522 if (child
->size
== validatedSize
)
525 update_disk_device_job_progress(job
, 0.0);
527 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
528 entry
.SetBlockCount(validatedSize
/ partition
->block_size
);
530 status_t result
= header
->WriteEntry(fd
, entryIndex
);
531 if (result
!= B_OK
) {
532 entry
.SetBlockCount(child
->size
/ partition
->block_size
);
536 child
->size
= validatedSize
;
538 update_disk_device_job_progress(job
, 1.0);
539 partition_modified(partitionID
);
545 efi_gpt_move(int fd
, partition_id partition
, off_t offset
, disk_job_id job
)
547 // nothing to do here
553 efi_gpt_move_child(int fd
, partition_id partitionID
, partition_id childID
,
554 off_t offset
, disk_job_id job
)
559 PartitionWriteLocker
locker(partitionID
);
560 if (!locker
.IsLocked())
563 partition_data
* partition
= get_partition(partitionID
);
564 if (partition
== NULL
)
567 partition_data
* child
= get_partition(childID
);
571 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
575 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
576 if (entryIndex
>= header
->EntryCount())
579 off_t validatedOffset
= offset
;
580 if (!efi_gpt_validate_move_child(partition
, child
, &validatedOffset
))
583 if (child
->offset
== validatedOffset
)
586 // TODO: implement actual moving, need to move the partition content
587 // (the raw data) here and need to take overlap into account
590 update_disk_device_job_progress(job
, 0.0);
592 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
593 uint64 blockCount
= entry
.BlockCount();
594 entry
.SetStartBlock((validatedOffset
- partition
->offset
)
595 / partition
->block_size
);
596 entry
.SetBlockCount(blockCount
);
598 status_t result
= header
->WriteEntry(fd
, entryIndex
);
599 if (result
!= B_OK
) {
600 // fatal error: the data has been moved but the partition table could
601 // not be updated to reflect that change!
605 child
->offset
= validatedOffset
;
607 update_disk_device_job_progress(job
, 1.0);
608 partition_modified(childID
);
614 efi_gpt_set_name(int fd
, partition_id partitionID
, const char* name
,
620 PartitionWriteLocker
locker(partitionID
);
621 if (!locker
.IsLocked())
624 partition_data
* child
= get_partition(partitionID
);
628 partition_data
* partition
= get_parent_partition(partitionID
);
629 if (partition
== NULL
)
632 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
636 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
637 if (entryIndex
>= header
->EntryCount())
640 update_disk_device_job_progress(job
, 0.0);
642 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
643 to_ucs2(name
, strlen(name
), entry
.name
, EFI_PARTITION_NAME_LENGTH
);
645 status_t result
= header
->WriteEntry(fd
, entryIndex
);
649 char newName
[B_OS_NAME_LENGTH
];
650 to_utf8(entry
.name
, EFI_PARTITION_NAME_LENGTH
, newName
, sizeof(newName
));
651 child
->name
= strdup(newName
);
653 update_disk_device_job_progress(job
, 1.0);
654 partition_modified(partitionID
);
660 efi_gpt_set_type(int fd
, partition_id partitionID
, const char* type
,
666 PartitionWriteLocker
locker(partitionID
);
667 if (!locker
.IsLocked())
670 partition_data
* child
= get_partition(partitionID
);
674 partition_data
* partition
= get_parent_partition(partitionID
);
675 if (partition
== NULL
)
678 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
682 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
683 if (entryIndex
>= header
->EntryCount())
687 if (!get_guid_for_partition_type(type
, typeGUID
))
690 update_disk_device_job_progress(job
, 0.0);
692 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
693 entry
.partition_type
= typeGUID
;
695 status_t result
= header
->WriteEntry(fd
, entryIndex
);
699 child
->type
= strdup(type
);
701 update_disk_device_job_progress(job
, 1.0);
702 partition_modified(partitionID
);
708 efi_gpt_initialize(int fd
, partition_id partitionID
, const char* name
,
709 const char* parameters
, off_t partitionSize
, disk_job_id job
)
714 partition_data
* partition
= get_partition(partitionID
);
715 if (partition
== NULL
)
718 update_disk_device_job_progress(job
, 0.0);
720 EFI::Header
header((partitionSize
- 1) / partition
->block_size
,
721 partition
->block_size
);
722 status_t result
= header
.InitCheck();
726 result
= header
.Write(fd
);
730 result
= scan_partition(partitionID
);
734 update_disk_device_job_progress(job
, 1.0);
735 partition_modified(partitionID
);
741 efi_gpt_uninitialize(int fd
, partition_id partitionID
, off_t partitionSize
,
742 uint32 blockSize
, disk_job_id job
)
747 partition_data
* partition
= get_partition(partitionID
);
748 if (partition
== NULL
)
751 update_disk_device_job_progress(job
, 0.0);
753 const int headerSize
= partition
->block_size
* 3;
754 // The first block is the protective MBR
755 // The second block is the GPT header
756 // The third block is the start of the partition list (it can span more
757 // blocks, but that doesn't matter as soon as the header is erased).
759 uint8 buffer
[headerSize
];
760 memset(buffer
, 0xE5, sizeof(buffer
));
762 // Erase the first blocks
763 if (write_pos(fd
, 0, &buffer
, headerSize
) < 0)
766 // Erase the last blocks
767 // Only 2 blocks, as there is no protective MBR
768 if (write_pos(fd
, partitionSize
- 2 * partition
->block_size
,
769 &buffer
, 2 * partition
->block_size
) < 0) {
773 update_disk_device_job_progress(job
, 1.0);
780 efi_gpt_create_child(int fd
, partition_id partitionID
, off_t offset
,
781 off_t size
, const char* type
, const char* name
, const char* parameters
,
782 disk_job_id job
, partition_id
* childID
)
787 PartitionWriteLocker
locker(partitionID
);
788 if (!locker
.IsLocked())
791 partition_data
* partition
= get_partition(partitionID
);
792 if (partition
== NULL
)
795 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
799 off_t validatedOffset
= offset
;
800 off_t validatedSize
= size
;
801 uint32 entryIndex
= 0;
803 if (!efi_gpt_validate_create_child(partition
, &validatedOffset
,
804 &validatedSize
, type
, name
, parameters
, (int32
*)&entryIndex
))
808 if (!get_guid_for_partition_type(type
, typeGUID
))
811 update_disk_device_job_progress(job
, 0.0);
813 partition_data
* child
= create_child_partition(partition
->id
, entryIndex
,
814 validatedOffset
, validatedSize
, *childID
);
818 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
819 entry
.partition_type
= typeGUID
;
821 uuid_generate_random(uuid
);
822 memcpy((uint8
*)&entry
.unique_guid
, uuid
, sizeof(guid_t
));
823 to_ucs2(name
, strlen(name
), entry
.name
, EFI_PARTITION_NAME_LENGTH
);
824 entry
.SetStartBlock((validatedOffset
- partition
->offset
)
825 / partition
->block_size
);
826 entry
.SetBlockCount(validatedSize
/ partition
->block_size
);
827 entry
.SetAttributes(0); // TODO
829 status_t result
= header
->WriteEntry(fd
, entryIndex
);
830 if (result
!= B_OK
) {
831 delete_partition(child
->id
);
835 *childID
= child
->id
;
836 child
->block_size
= partition
->block_size
;
837 child
->name
= strdup(name
);
838 child
->type
= strdup(type
);
839 child
->parameters
= strdup(parameters
);
840 child
->cookie
= (void*)(addr_t
)entryIndex
;
842 if (child
->type
== NULL
|| child
->parameters
== NULL
) {
843 delete_partition(child
->id
);
847 update_disk_device_job_progress(job
, 1.0);
848 partition_modified(partitionID
);
854 efi_gpt_delete_child(int fd
, partition_id partitionID
, partition_id childID
,
860 PartitionWriteLocker
locker(partitionID
);
861 if (!locker
.IsLocked())
864 partition_data
* partition
= get_partition(partitionID
);
865 if (partition
== NULL
)
868 partition_data
* child
= get_partition(childID
);
872 EFI::Header
* header
= (EFI::Header
*)partition
->content_cookie
;
876 uint32 entryIndex
= (uint32
)(addr_t
)child
->cookie
;
877 if (entryIndex
>= header
->EntryCount())
880 update_disk_device_job_progress(job
, 0.0);
882 if (!delete_partition(childID
))
885 efi_partition_entry
& entry
= header
->EntryAt(entryIndex
);
886 memset(&entry
, 0, sizeof(efi_partition_entry
));
887 entry
.partition_type
= kEmptyGUID
;
889 status_t result
= header
->WriteEntry(fd
, entryIndex
);
893 update_disk_device_job_progress(job
, 1.0);
894 partition_modified(partitionID
);
897 #endif // !_BOOT_MODE
901 static partition_module_info sEFIPartitionModule
= {
903 partition_module_info gEFIPartitionModule
= {
906 EFI_PARTITION_MODULE_NAME
,
911 EFI_PARTITION_NAME
, // pretty_name
913 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING
914 | B_DISK_SYSTEM_SUPPORTS_MOVING
915 | B_DISK_SYSTEM_SUPPORTS_RESIZING
916 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
917 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
918 | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
919 | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
920 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
921 | B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
922 | B_DISK_SYSTEM_SUPPORTS_NAME
926 efi_gpt_identify_partition
,
927 efi_gpt_scan_partition
,
928 efi_gpt_free_identify_partition_cookie
,
929 NULL
, // free_partition_cookie
930 efi_gpt_free_partition_content_cookie
,
934 efi_gpt_get_supported_operations
,
935 efi_gpt_get_supported_child_operations
,
936 NULL
, // supports_initializing_child
937 efi_gpt_is_sub_system_for
,
939 efi_gpt_validate_resize
,
940 efi_gpt_validate_resize_child
,
941 efi_gpt_validate_move
,
942 efi_gpt_validate_move_child
,
943 efi_gpt_validate_set_name
,
944 NULL
, // validate_set_content_name
945 efi_gpt_validate_set_type
,
946 NULL
, // validate_set_parameters
947 NULL
, // validate_set_content_parameters
948 efi_gpt_validate_initialize
,
949 efi_gpt_validate_create_child
,
950 efi_gpt_get_partitionable_spaces
,
951 efi_gpt_get_next_supported_type
,
952 NULL
, // get_type_for_content_type
954 // shadow partition modification
955 efi_gpt_shadow_changed
,
960 efi_gpt_resize_child
,
964 NULL
, // set_content_name
966 NULL
, // set_parameters
967 NULL
, // set_content_parameters
969 efi_gpt_uninitialize
,
970 efi_gpt_create_child
,
978 partition_module_info
* modules
[] = {
979 &sEFIPartitionModule
,