1/*
2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
3 * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8
9#include "efi_gpt.h"
10
11#include <KernelExport.h>
12#include <disk_device_manager/ddm_modules.h>
13#include <disk_device_types.h>
14#ifdef _BOOT_MODE
15#	include <boot/partitions.h>
16#else
17#	include <DiskDeviceTypes.h>
18#	include "PartitionLocker.h"
19#endif
20#include <util/kernel_cpp.h>
21
22#include <unistd.h>
23#include <stdio.h>
24#include <string.h>
25
26#include "Header.h"
27#include "utility.h"
28
29
30#define TRACE_EFI_GPT
31#ifdef TRACE_EFI_GPT
32#	define TRACE(x) dprintf x
33#else
34#	define TRACE(x) ;
35#endif
36
37
38#define EFI_PARTITION_MODULE_NAME "partitioning_systems/efi_gpt/v1"
39
40
41#ifndef _BOOT_MODE
42static off_t
43block_align(partition_data* partition, off_t offset, bool upwards)
44{
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);
48	if (upwards)
49		return ((offset + blockSize - 1) / blockSize) * blockSize;
50
51	return (offset / blockSize) * blockSize;
52}
53#endif // !_BOOT_MODE
54
55
56//	#pragma mark - public module interface
57
58
59static status_t
60efi_gpt_std_ops(int32 op, ...)
61{
62	switch (op) {
63		case B_MODULE_INIT:
64		case B_MODULE_UNINIT:
65			return B_OK;
66	}
67
68	return B_ERROR;
69}
70
71
72static float
73efi_gpt_identify_partition(int fd, partition_data* partition, void** _cookie)
74{
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();
78	if (status != B_OK) {
79		delete header;
80		return -1;
81	}
82
83	*_cookie = header;
84	return 0.96;
85		// This must be higher as Intel partitioning, as EFI can contain this
86		// partitioning for compatibility
87}
88
89
90static status_t
91efi_gpt_scan_partition(int fd, partition_data* partition, void* _cookie)
92{
93	TRACE(("efi_gpt_scan_partition(cookie = %p)\n", _cookie));
94	EFI::Header* header = (EFI::Header*)_cookie;
95
96	partition->status = B_PARTITION_VALID;
97	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
98	partition->content_size = partition->size;
99	partition->content_cookie = header;
100
101	// scan all children
102
103	uint32 index = 0;
104
105	for (uint32 i = 0; i < header->EntryCount(); i++) {
106		const efi_partition_entry& entry = header->EntryAt(i);
107
108		if (entry.partition_type == kEmptyGUID)
109			continue;
110
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()));
115			continue;
116		}
117
118		partition_data* child = create_child_partition(partition->id, index++,
119			partition->offset + entry.StartBlock() * partition->block_size,
120			entry.BlockCount() * partition->block_size, -1);
121		if (child == NULL) {
122			TRACE(("efi_gpt: Creating child at index %ld failed\n", index - 1));
123			return B_ERROR;
124		}
125
126		char name[B_OS_NAME_LENGTH];
127		to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name));
128		child->name = strdup(name);
129		child->type = strdup(get_partition_type(entry.partition_type));
130		child->block_size = partition->block_size;
131		child->cookie = (void*)i;
132	}
133
134	return B_OK;
135}
136
137
138static void
139efi_gpt_free_identify_partition_cookie(partition_data* partition, void* _cookie)
140{
141	// Cookie is freed in efi_gpt_free_partition_content_cookie().
142}
143
144
145static void
146efi_gpt_free_partition_content_cookie(partition_data* partition)
147{
148	delete (EFI::Header*)partition->content_cookie;
149}
150
151
152#ifndef _BOOT_MODE
153static uint32
154efi_gpt_get_supported_operations(partition_data* partition, uint32 mask)
155{
156	uint32 flags = B_DISK_SYSTEM_SUPPORTS_INITIALIZING
157		| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
158		| B_DISK_SYSTEM_SUPPORTS_MOVING
159		| B_DISK_SYSTEM_SUPPORTS_RESIZING
160		| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
161		// TODO: check for available entries and partitionable space and only
162		// add creating child support if both is valid
163
164	return flags;
165}
166
167
168static uint32
169efi_gpt_get_supported_child_operations(partition_data* partition,
170	partition_data* child, uint32 mask)
171{
172	return B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
173		| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
174		| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
175		| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
176}
177
178
179static bool
180efi_gpt_is_sub_system_for(partition_data* partition)
181{
182	// a GUID Partition Table doesn't usually live inside another partition
183	return false;
184}
185
186
187static bool
188efi_gpt_validate_resize(partition_data* partition, off_t* size)
189{
190	off_t newSize = *size;
191	if (newSize == partition->size)
192		return true;
193
194	if (newSize < 0)
195		newSize = 0;
196	else
197		newSize = block_align(partition, newSize, false);
198
199	// growing
200	if (newSize > partition->size) {
201		*size = newSize;
202		return true;
203	}
204
205	// shrinking, only so that no child would be truncated
206	off_t newEnd = partition->offset + newSize;
207	for (int32 i = 0; i < partition->child_count; i++) {
208		partition_data* child = get_child_partition(partition->id, i);
209		if (child == NULL)
210			continue;
211
212		if (child->offset + child->size > newEnd)
213			newEnd = child->offset + child->size;
214	}
215
216	newSize = block_align(partition, newEnd - partition->offset, true);
217	*size = newSize;
218	return true;
219}
220
221
222static bool
223efi_gpt_validate_resize_child(partition_data* partition, partition_data* child,
224	off_t* size)
225{
226	off_t newSize = *size;
227	if (newSize == child->size)
228		return true;
229
230	// shrinking
231	if (newSize < child->size) {
232		if (newSize < 0)
233			newSize = 0;
234
235		*size = block_align(partition, newSize, false);
236		return true;
237	}
238
239	// growing, but only so much that the child doesn't get bigger than
240	// the parent
241	if (child->offset + newSize > partition->offset + partition->size)
242		newSize = partition->offset + partition->size - child->offset;
243
244	// make sure that the child doesn't overlap any sibling partitions
245	off_t newEnd = child->offset + newSize;
246	for (int32 i = 0; i < partition->child_count; i++) {
247		partition_data* other = get_child_partition(partition->id, i);
248		if (other == NULL || other->id == child->id
249			|| other->offset < child->offset)
250			continue;
251
252		if (newEnd > other->offset)
253			newEnd = other->offset;
254	}
255
256	*size = block_align(partition, newEnd - child->offset, false);
257	return true;
258}
259
260
261static bool
262efi_gpt_validate_move(partition_data* partition, off_t* start)
263{
264	// nothing to do
265	return true;
266}
267
268
269static bool
270efi_gpt_validate_move_child(partition_data* partition, partition_data* child,
271	off_t* start)
272{
273	off_t newStart = *start;
274	if (newStart < 0)
275		newStart = 0;
276
277	if (newStart + child->size > partition->size)
278		newStart = partition->size - child->size;
279
280	newStart = block_align(partition, newStart, false);
281	if (newStart > child->offset) {
282		for (int32 i = 0; i < partition->child_count; i++) {
283			partition_data* other = get_child_partition(partition->id, i);
284			if (other == NULL || other->id == child->id
285				|| other->offset < child->offset)
286				continue;
287
288			if (other->offset < newStart + child->size)
289				newStart = other->offset - child->size;
290		}
291
292		newStart = block_align(partition, newStart, false);
293	} else {
294		for (int32 i = 0; i < partition->child_count; i++) {
295			partition_data* other = get_child_partition(partition->id, i);
296			if (other == NULL || other->id == child->id
297				|| other->offset > child->offset)
298				continue;
299
300			if (other->offset + other->size > newStart)
301				newStart = other->offset + other->size;
302		}
303
304		newStart = block_align(partition, newStart, true);
305	}
306
307	*start = newStart;
308	return true;
309}
310
311
312static bool
313efi_gpt_validate_set_content_name(partition_data* partition, char* name)
314{
315	// TODO: should validate that the utf-8 -> ucs-2 is valid
316	// TODO: should count actual utf-8 chars
317	if (strlen(name) > EFI_PARTITION_NAME_LENGTH)
318		name[EFI_PARTITION_NAME_LENGTH - 1] = 0;
319	return true;
320}
321
322
323static bool
324efi_gpt_validate_set_type(partition_data* partition, const char* type)
325{
326	guid_t typeGUID;
327	return get_guid_for_partition_type(type, typeGUID);
328}
329
330
331static bool
332efi_gpt_validate_initialize(partition_data* partition, char* name,
333	const char* parameters)
334{
335	if ((efi_gpt_get_supported_operations(partition, ~0)
336		& B_DISK_SYSTEM_SUPPORTS_INITIALIZING) == 0)
337		return false;
338
339	// name and parameters are ignored
340	if (name != NULL)
341		name[0] = 0;
342
343	return true;
344}
345
346
347static bool
348efi_gpt_validate_create_child(partition_data* partition, off_t* start,
349	off_t* size, const char* type, const char* name, const char* parameters,
350	int32* index)
351{
352	if ((efi_gpt_get_supported_operations(partition, ~0)
353			& B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) == 0)
354		return false;
355
356	if (!efi_gpt_validate_set_type(partition, type))
357		return false;
358
359	EFI::Header* header = (EFI::Header*)partition->content_cookie;
360	int32 entryIndex = -1;
361	for (uint32 i = 0; i < header->EntryCount(); i++) {
362		const efi_partition_entry& entry = header->EntryAt(i);
363		if (entry.partition_type == kEmptyGUID) {
364			entryIndex = i;
365			break;
366		}
367	}
368
369	if (entryIndex < 0)
370		return false;
371
372	*index = entryIndex;
373
374	// ensure that child lies between first and last usable block
375	off_t firstUsable = header->FirstUsableBlock() * partition->block_size;
376	if (*start < firstUsable)
377		*start = firstUsable;
378
379	off_t lastUsable = header->LastUsableBlock() * partition->block_size;
380	if (*start + *size > lastUsable) {
381		if (*start > lastUsable)
382			return false;
383
384		*size = lastUsable - *start;
385	}
386
387	// ensure that we don't overlap any siblings
388	for (int32 i = 0; i < partition->child_count; i++) {
389		partition_data* other = get_child_partition(partition->id, i);
390		if (other == NULL)
391			continue;
392
393		if (other->offset < *start && other->offset + other->size > *start)
394			*start = other->offset + other->size;
395
396		if (other->offset > *start && other->offset < *start + *size)
397			*size = other->offset - *start;
398	}
399
400	*start = block_align(partition, *start, true);
401	*size = block_align(partition, *size, false);
402
403	// TODO: support parameters
404	return true;
405}
406
407
408static status_t
409efi_gpt_get_partitionable_spaces(partition_data* partition,
410	partitionable_space_data* buffer, int32 count, int32* actualCount)
411{
412	// TODO: implement
413	return B_ERROR;
414}
415
416
417static status_t
418efi_gpt_get_next_supported_type(partition_data* partition, int32* cookie,
419	char* type)
420{
421	// TODO: implement
422	return B_ERROR;
423}
424
425
426static status_t
427efi_gpt_shadow_changed(partition_data* partition, partition_data* child,
428	uint32 operation)
429{
430	// TODO: implement
431	return B_ERROR;
432}
433
434
435static status_t
436efi_gpt_repair(int fd, partition_id partition, bool checkOnly, disk_job_id job)
437{
438	// TODO: implement, validate CRCs and restore from backup area if corrupt
439	return B_ERROR;
440}
441
442
443static status_t
444efi_gpt_resize(int fd, partition_id partitionID, off_t size, disk_job_id job)
445{
446	if (fd < 0)
447		return B_ERROR;
448
449	PartitionWriteLocker locker(partitionID);
450	if (!locker.IsLocked())
451		return B_ERROR;
452
453	partition_data* partition = get_partition(partitionID);
454	if (partition == NULL)
455		return B_BAD_VALUE;
456
457	off_t validatedSize = size;
458	if (!efi_gpt_validate_resize(partition, &validatedSize))
459		return B_BAD_VALUE;
460
461	update_disk_device_job_progress(job, 0.0);
462
463	partition->size = validatedSize;
464	partition->content_size = validatedSize;
465
466	update_disk_device_job_progress(job, 1.0);
467	partition_modified(partitionID);
468	return B_OK;
469}
470
471
472static status_t
473efi_gpt_resize_child(int fd, partition_id partitionID, off_t size,
474	disk_job_id job)
475{
476	if (fd < 0)
477		return B_ERROR;
478
479	PartitionWriteLocker locker(partitionID);
480	if (!locker.IsLocked())
481		return B_ERROR;
482
483	partition_data* child = get_partition(partitionID);
484	if (child == NULL)
485		return B_BAD_VALUE;
486
487	partition_data* partition = get_parent_partition(partitionID);
488	if (partition == NULL)
489		return B_BAD_VALUE;
490
491	EFI::Header* header = (EFI::Header*)partition->content_cookie;
492	if (header == NULL)
493		return B_BAD_VALUE;
494
495	uint32 entryIndex = (uint32)child->cookie;
496	if (entryIndex >= header->EntryCount())
497		return B_BAD_VALUE;
498
499	off_t validatedSize = size;
500	if (!efi_gpt_validate_resize_child(partition, child, &validatedSize))
501		return B_BAD_VALUE;
502
503	if (child->size == validatedSize)
504		return B_OK;
505
506	update_disk_device_job_progress(job, 0.0);
507
508	efi_partition_entry& entry = header->EntryAt(entryIndex);
509	entry.SetBlockCount(validatedSize / partition->block_size);
510
511	status_t result = header->WriteEntry(fd, entryIndex);
512	if (result != B_OK) {
513		entry.SetBlockCount(child->size / partition->block_size);
514		return result;
515	}
516
517	child->size = validatedSize;
518
519	update_disk_device_job_progress(job, 1.0);
520	partition_modified(partitionID);
521	return B_OK;
522}
523
524
525static status_t
526efi_gpt_move(int fd, partition_id partition, off_t offset, disk_job_id job)
527{
528	// nothing to do here
529	return B_OK;
530}
531
532
533static status_t
534efi_gpt_move_child(int fd, partition_id partitionID, partition_id childID,
535	off_t offset, disk_job_id job)
536{
537	if (fd < 0)
538		return B_ERROR;
539
540	PartitionWriteLocker locker(partitionID);
541	if (!locker.IsLocked())
542		return B_ERROR;
543
544	partition_data* partition = get_partition(partitionID);
545	if (partition == NULL)
546		return B_BAD_VALUE;
547
548	partition_data* child = get_partition(childID);
549	if (child == NULL)
550		return B_BAD_VALUE;
551
552	EFI::Header* header = (EFI::Header*)partition->content_cookie;
553	if (header == NULL)
554		return B_BAD_VALUE;
555
556	uint32 entryIndex = (uint32)child->cookie;
557	if (entryIndex >= header->EntryCount())
558		return B_BAD_VALUE;
559
560	off_t validatedOffset = offset;
561	if (!efi_gpt_validate_move_child(partition, child, &validatedOffset))
562		return B_BAD_VALUE;
563
564	if (child->offset == validatedOffset)
565		return B_OK;
566
567	// TODO: implement actual moving, need to move the partition content
568	// (the raw data) here and need to take overlap into account
569	return B_ERROR;
570
571	update_disk_device_job_progress(job, 0.0);
572
573	efi_partition_entry& entry = header->EntryAt(entryIndex);
574	uint64 blockCount = entry.BlockCount();
575	entry.SetStartBlock((validatedOffset - partition->offset)
576		/ partition->block_size);
577	entry.SetBlockCount(blockCount);
578
579	status_t result = header->WriteEntry(fd, entryIndex);
580	if (result != B_OK) {
581		// fatal error: the data has been moved but the partition table could
582		// not be updated to reflect that change!
583		return result;
584	}
585
586	child->offset = validatedOffset;
587
588	update_disk_device_job_progress(job, 1.0);
589	partition_modified(childID);
590	return B_OK;
591}
592
593
594static status_t
595efi_gpt_set_content_name(int fd, partition_id partitionID, const char* name,
596	disk_job_id job)
597{
598	if (fd < 0)
599		return B_ERROR;
600
601	PartitionWriteLocker locker(partitionID);
602	if (!locker.IsLocked())
603		return B_ERROR;
604
605	partition_data* child = get_partition(partitionID);
606	if (child == NULL)
607		return B_BAD_VALUE;
608
609	partition_data* partition = get_parent_partition(partitionID);
610	if (partition == NULL)
611		return B_BAD_VALUE;
612
613	EFI::Header* header = (EFI::Header*)partition->content_cookie;
614	if (header == NULL)
615		return B_BAD_VALUE;
616
617	uint32 entryIndex = (uint32)child->cookie;
618	if (entryIndex >= header->EntryCount())
619		return B_BAD_VALUE;
620
621	update_disk_device_job_progress(job, 0.0);
622
623	efi_partition_entry& entry = header->EntryAt(entryIndex);
624	to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH);
625
626	status_t result = header->WriteEntry(fd, entryIndex);
627	if (result != B_OK)
628		return result;
629
630	char newName[B_OS_NAME_LENGTH];
631	to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, newName, sizeof(newName));
632	child->name = strdup(newName);
633
634	update_disk_device_job_progress(job, 1.0);
635	partition_modified(partitionID);
636	return B_OK;
637}
638
639
640static status_t
641efi_gpt_set_type(int fd, partition_id partitionID, const char* type,
642	disk_job_id job)
643{
644	if (fd < 0)
645		return B_ERROR;
646
647	PartitionWriteLocker locker(partitionID);
648	if (!locker.IsLocked())
649		return B_ERROR;
650
651	partition_data* child = get_partition(partitionID);
652	if (child == NULL)
653		return B_BAD_VALUE;
654
655	partition_data* partition = get_parent_partition(partitionID);
656	if (partition == NULL)
657		return B_BAD_VALUE;
658
659	EFI::Header* header = (EFI::Header*)partition->content_cookie;
660	if (header == NULL)
661		return B_BAD_VALUE;
662
663	uint32 entryIndex = (uint32)child->cookie;
664	if (entryIndex >= header->EntryCount())
665		return B_BAD_VALUE;
666
667	guid_t typeGUID;
668	if (!get_guid_for_partition_type(type, typeGUID))
669		return B_BAD_VALUE;
670
671	update_disk_device_job_progress(job, 0.0);
672
673	efi_partition_entry& entry = header->EntryAt(entryIndex);
674	entry.partition_type = typeGUID;
675
676	status_t result = header->WriteEntry(fd, entryIndex);
677	if (result != B_OK)
678		return result;
679
680	child->type = strdup(type);
681
682	update_disk_device_job_progress(job, 1.0);
683	partition_modified(partitionID);
684	return B_OK;
685}
686
687
688static status_t
689efi_gpt_initialize(int fd, partition_id partitionID, const char* name,
690	const char* parameters, off_t partitionSize, disk_job_id job)
691{
692	if (fd < 0)
693		return B_ERROR;
694
695	partition_data* partition = get_partition(partitionID);
696	if (partition == NULL)
697		return B_BAD_VALUE;
698
699	update_disk_device_job_progress(job, 0.0);
700
701	EFI::Header header((partitionSize - 1) / partition->block_size,
702		partition->block_size);
703	status_t result = header.InitCheck();
704	if (result != B_OK)
705		return result;
706
707	result = header.Write(fd);
708	if (result != B_OK)
709		return result;
710
711	result = scan_partition(partitionID);
712	if (result != B_OK)
713		return result;
714
715	update_disk_device_job_progress(job, 1.0);
716	partition_modified(partitionID);
717	return B_OK;
718}
719
720
721static status_t
722efi_gpt_create_child(int fd, partition_id partitionID, off_t offset,
723	off_t size, const char* type, const char* name, const char* parameters,
724	disk_job_id job, partition_id* childID)
725{
726	if (fd < 0)
727		return B_ERROR;
728
729	PartitionWriteLocker locker(partitionID);
730	if (!locker.IsLocked())
731		return B_ERROR;
732
733	partition_data* partition = get_partition(partitionID);
734	if (partition == NULL)
735		return B_BAD_VALUE;
736
737	EFI::Header* header = (EFI::Header*)partition->content_cookie;
738	if (header == NULL)
739		return B_BAD_VALUE;
740
741	off_t validatedOffset = offset;
742	off_t validatedSize = size;
743	uint32 entryIndex = 0;
744
745	if (!efi_gpt_validate_create_child(partition, &validatedOffset,
746			&validatedSize, type, name, parameters, (int32*)&entryIndex))
747		return B_BAD_VALUE;
748
749	guid_t typeGUID;
750	if (!get_guid_for_partition_type(type, typeGUID))
751		return B_BAD_VALUE;
752
753	update_disk_device_job_progress(job, 0.0);
754
755	partition_data* child = create_child_partition(partition->id, entryIndex,
756		validatedOffset, validatedSize, *childID);
757	if (child == NULL)
758		return B_ERROR;
759
760	efi_partition_entry& entry = header->EntryAt(entryIndex);
761	entry.partition_type = typeGUID;
762	// TODO: set unique partition ID
763	to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH);
764	entry.SetStartBlock((validatedOffset - partition->offset)
765		/ partition->block_size);
766	entry.SetBlockCount(validatedSize / partition->block_size);
767	entry.SetAttributes(0); // TODO
768
769	status_t result = header->WriteEntry(fd, entryIndex);
770	if (result != B_OK) {
771		delete_partition(child->id);
772		return result;
773	}
774
775	*childID = child->id;
776	child->block_size = partition->block_size;
777	child->name = strdup(name);
778	child->type = strdup(type);
779	child->parameters = strdup(parameters);
780	child->cookie = (void*)entryIndex;
781
782	if (child->type == NULL || child->parameters == NULL) {
783		delete_partition(child->id);
784		return B_NO_MEMORY;
785	}
786
787	update_disk_device_job_progress(job, 1.0);
788	partition_modified(partitionID);
789	return B_OK;
790}
791
792
793static status_t
794efi_gpt_delete_child(int fd, partition_id partitionID, partition_id childID,
795	disk_job_id job)
796{
797	if (fd < 0)
798		return B_ERROR;
799
800	PartitionWriteLocker locker(partitionID);
801	if (!locker.IsLocked())
802		return B_ERROR;
803
804	partition_data* partition = get_partition(partitionID);
805	if (partition == NULL)
806		return B_BAD_VALUE;
807
808	partition_data* child = get_partition(childID);
809	if (child == NULL)
810		return B_BAD_VALUE;
811
812	EFI::Header* header = (EFI::Header*)partition->content_cookie;
813	if (header == NULL)
814		return B_BAD_VALUE;
815
816	uint32 entryIndex = (uint32)child->cookie;
817	if (entryIndex >= header->EntryCount())
818		return B_BAD_VALUE;
819
820	update_disk_device_job_progress(job, 0.0);
821
822	if (!delete_partition(childID))
823		return B_ERROR;
824
825	efi_partition_entry& entry = header->EntryAt(entryIndex);
826	entry.partition_type = kEmptyGUID;
827
828	status_t result = header->WriteEntry(fd, entryIndex);
829	if (result != B_OK)
830		return result;
831
832	update_disk_device_job_progress(job, 1.0);
833	partition_modified(partitionID);
834	return B_OK;
835}
836#endif // !_BOOT_MODE
837
838
839#ifndef _BOOT_MODE
840static partition_module_info sEFIPartitionModule = {
841#else
842partition_module_info gEFIPartitionModule = {
843#endif
844	{
845		EFI_PARTITION_MODULE_NAME,
846		0,
847		efi_gpt_std_ops
848	},
849	"gpt",									// short_name
850	EFI_PARTITION_NAME,						// pretty_name
851	0										// flags
852	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
853	| B_DISK_SYSTEM_SUPPORTS_MOVING
854	| B_DISK_SYSTEM_SUPPORTS_RESIZING
855	| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
856	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
857	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
858	| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
859	| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
860	| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
861	| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
862	,
863
864	// scanning
865	efi_gpt_identify_partition,
866	efi_gpt_scan_partition,
867	efi_gpt_free_identify_partition_cookie,
868	NULL, // free_partition_cookie
869	efi_gpt_free_partition_content_cookie,
870
871#ifndef _BOOT_MODE
872	// querying
873	efi_gpt_get_supported_operations,
874	efi_gpt_get_supported_child_operations,
875	NULL, // supports_initializing_child
876	efi_gpt_is_sub_system_for,
877
878	efi_gpt_validate_resize,
879	efi_gpt_validate_resize_child,
880	efi_gpt_validate_move,
881	efi_gpt_validate_move_child,
882	NULL, // validate_set_name
883	efi_gpt_validate_set_content_name,
884	efi_gpt_validate_set_type,
885	NULL, // validate_set_parameters
886	NULL, // validate_set_content_parameters
887	efi_gpt_validate_initialize,
888	efi_gpt_validate_create_child,
889	efi_gpt_get_partitionable_spaces,
890	efi_gpt_get_next_supported_type,
891	NULL, // get_type_for_content_type
892
893	// shadow partition modification
894	efi_gpt_shadow_changed,
895
896	// writing
897	efi_gpt_repair,
898	efi_gpt_resize,
899	efi_gpt_resize_child,
900	efi_gpt_move,
901	efi_gpt_move_child,
902	NULL, // set_name
903	efi_gpt_set_content_name,
904	efi_gpt_set_type,
905	NULL, // set_parameters
906	NULL, // set_content_parameters
907	efi_gpt_initialize,
908	NULL, // uninitialize
909	efi_gpt_create_child,
910	efi_gpt_delete_child
911#else
912	NULL
913#endif // _BOOT_MODE
914};
915
916#ifndef _BOOT_MODE
917partition_module_info* modules[] = {
918	&sEFIPartitionModule,
919	NULL
920};
921#endif
922