diff --git a/src/add-ons/kernel/partitioning_systems/efi/Jamfile b/src/add-ons/kernel/partitioning_systems/efi/Jamfile index 3edb361748..9eeb4fe285 100644 --- a/src/add-ons/kernel/partitioning_systems/efi/Jamfile +++ b/src/add-ons/kernel/partitioning_systems/efi/Jamfile @@ -6,4 +6,5 @@ UsePrivateHeaders [ FDirName storage ] ; KernelAddon efi_gpt : efi_gpt.cpp + PartitionLocker.cpp ; diff --git a/src/add-ons/kernel/partitioning_systems/efi/PartitionLocker.cpp b/src/add-ons/kernel/partitioning_systems/efi/PartitionLocker.cpp new file mode 100644 index 0000000000..b7690f095d --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/efi/PartitionLocker.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2003-2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Tomas Kucera, kucerat@centrum.cz + */ + +#include "PartitionLocker.h" + +// #pragma mark - PartitionLocker + +// constructor +PartitionLocker::PartitionLocker(partition_id partitionID) + : fDevice(NULL), + fPartitionID(partitionID) +{ +} + +// destructor +PartitionLocker::~PartitionLocker() +{ +} + +// IsLocked +bool +PartitionLocker::IsLocked() const +{ + return fDevice; +} + +// PartitionId +partition_id +PartitionLocker::PartitionId() const +{ + return fPartitionID; +} + + +// #pragma mark - PartitionReadLocker + + +// constructor +PartitionReadLocker::PartitionReadLocker(partition_id partitionID) + : PartitionLocker(partitionID) +{ + fDevice = read_lock_disk_device(partitionID); +} + +// destructor +PartitionReadLocker::~PartitionReadLocker() +{ + if (IsLocked()) + read_unlock_disk_device(PartitionId()); +} + + +// #pragma mark - PartitionWriteLocker + + +// constructor +PartitionWriteLocker::PartitionWriteLocker(partition_id partitionID) + : PartitionLocker(partitionID) +{ + fDevice = write_lock_disk_device(partitionID); +} + +// destructor +PartitionWriteLocker::~PartitionWriteLocker() +{ + if (IsLocked()) + write_unlock_disk_device(PartitionId()); +} diff --git a/src/add-ons/kernel/partitioning_systems/efi/PartitionLocker.h b/src/add-ons/kernel/partitioning_systems/efi/PartitionLocker.h new file mode 100644 index 0000000000..698ce0b2e1 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/efi/PartitionLocker.h @@ -0,0 +1,63 @@ +/* + * Copyright 2003-2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Tomas Kucera, kucerat@centrum.cz + */ + +/*! + \file PartitionLocker.h + \ingroup intel_module + \brief Structures for easy locking and automatic unlocking partitions. + */ + +#ifndef _PARTITION_LOCKER_H +#define _PARTITION_LOCKER_H + +#include + + +class PartitionLocker { +public: + PartitionLocker(partition_id partitionID); + ~PartitionLocker(); + + bool IsLocked() const; + partition_id PartitionId() const; + +protected: + const disk_device_data *fDevice; + +private: + partition_id fPartitionID; +}; + + +/*! + \brief Structure which locks given partition for reading. + + When this structure is going to be destroyed, it automatically unlocks + that partition. +*/ +class PartitionReadLocker : public PartitionLocker { +public: + PartitionReadLocker(partition_id partitionID); + ~PartitionReadLocker(); +}; + + +/*! + \brief Structure which locks given partition for writing. + + When this structure is going to be destroyed, it automatically unlocks + that partition. +*/ +class PartitionWriteLocker : public PartitionLocker { +public: + PartitionWriteLocker(partition_id partitionID); + ~PartitionWriteLocker(); +}; + + +#endif // _PARTITION_LOCKER_H diff --git a/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.cpp b/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.cpp index 7478242c4d..28f1d303f3 100644 --- a/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.cpp +++ b/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved. * Copyright 2007-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ @@ -11,6 +12,7 @@ # include #else # include +# include "PartitionLocker.h" #endif #include @@ -53,18 +55,32 @@ namespace EFI { class Header { public: Header(int fd, off_t block, uint32 blockSize); +#ifndef _BOOT_MODE + // constructor for empty header + Header(off_t block, uint32 blockSize); +#endif ~Header(); status_t InitCheck() const; bool IsPrimary() const { return fBlock == EFI_HEADER_LOCATION; } + uint64 FirstUsableBlock() const + { return fHeader.FirstUsableBlock(); } + uint64 LastUsableBlock() const + { return fHeader.LastUsableBlock(); } + uint32 EntryCount() const { return fHeader.EntryCount(); } - const efi_partition_entry &EntryAt(int32 index) const - { return *(const efi_partition_entry*) + efi_partition_entry &EntryAt(int32 index) const + { return *(efi_partition_entry *) (fEntries + fHeader.EntrySize() * index); } +#ifndef _BOOT_MODE + status_t WriteEntry(int fd, uint32 entryIndex); + status_t Write(int fd); +#endif + private: #ifdef TRACE_EFI_GPT const char *_PrintGUID(const guid_t &id); @@ -141,7 +157,16 @@ to_utf8(const uint16 *from, size_t maxFromLength, char *to, size_t toSize) } -const char * +#ifndef _BOOT_MODE +static void +to_ucs2(const char *from, size_t fromLength, uint16 *to, size_t maxToLength) +{ + // TODO: implement +} +#endif // !_BOOT_MODE + + +static const char * get_partition_type(const guid_t &guid) { for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) { @@ -153,6 +178,32 @@ get_partition_type(const guid_t &guid) } +#ifndef _BOOT_MODE +static const static_guid * +guid_for_partition_type(const char *type) +{ + for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) { + if (strcmp(kTypeMap[i].type, type) == 0) + return &kTypeMap[i].guid; + } + + return NULL; +} + + +static off_t +block_align(partition_data *partition, off_t offset, bool upwards) +{ + if (upwards) { + return ((offset + partition->block_size - 1) / partition->block_size) + * partition->block_size; + } + + return (offset / partition->block_size) * partition->block_size; +} +#endif // !_BOOT_MODE + + // #pragma mark - @@ -224,6 +275,51 @@ Header::Header(int fd, off_t block, uint32 blockSize) } +#ifndef _BOOT_MODE +Header::Header(off_t block, uint32 blockSize) + : + fBlock(block), + fBlockSize(blockSize), + fStatus(B_NO_INIT), + fEntries(NULL) +{ + // initialize to an empty header + memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)); + fHeader.SetRevision(EFI_TABLE_REVISION); + fHeader.SetHeaderSize(sizeof(fHeader)); + fHeader.SetHeaderCRC(0); + fHeader.SetAbsoluteBlock(fBlock); + fHeader.SetAlternateBlock(0); // TODO + // TODO: set disk guid + fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK); + fHeader.SetEntryCount(EFI_PARTITION_ENTRY_COUNT); + fHeader.SetEntrySize(EFI_PARTITION_ENTRY_SIZE); + fHeader.SetEntriesCRC(0); + + size_t arraySize = _EntryArraySize(); + fEntries = new (std::nothrow) uint8[arraySize]; + if (fEntries == NULL) { + fStatus = B_NO_MEMORY; + return; + } + + memset(fEntries, 0, arraySize); + // TODO: initialize the entry guids + + fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + + (arraySize + fBlockSize - 1) / fBlockSize); + fHeader.SetLastUsableBlock(0); // TODO + +#ifdef TRACE_EFI_GPT + _Dump(); + _DumpPartitions(); +#endif + + fStatus = B_OK; +} +#endif // !_BOOT_MODE + + Header::~Header() { delete[] fEntries; @@ -237,6 +333,24 @@ Header::InitCheck() const } +#ifndef _BOOT_MODE +status_t +Header::WriteEntry(int fd, uint32 entryIndex) +{ + // TODO: implement + return B_ERROR; +} + + +status_t +Header::Write(int fd) +{ + // TODO: implement + return B_ERROR; +} +#endif // !_BOOT_MODE + + bool Header::_ValidateCRC(uint8 *data, size_t size) const { @@ -348,6 +462,7 @@ efi_gpt_scan_partition(int fd, partition_data *partition, void *_cookie) partition->status = B_PARTITION_VALID; partition->flags |= B_PARTITION_PARTITIONING_SYSTEM | B_PARTITION_READ_ONLY; partition->content_size = partition->size; + partition->content_cookie = header; // scan all children @@ -381,9 +496,9 @@ efi_gpt_scan_partition(int fd, partition_data *partition, void *_cookie) child->offset = partition->offset + entry.StartBlock() * partition->block_size; - child->size = (entry.EndBlock() - entry.StartBlock()) - * partition->block_size; + child->size = entry.BlockCount() * partition->block_size; child->block_size = partition->block_size; + child->cookie = (void *)i; } return B_OK; @@ -391,12 +506,694 @@ efi_gpt_scan_partition(int fd, partition_data *partition, void *_cookie) static void -efi_gpt_free_identify_partition_cookie(partition_data *partition, void *_cookie) +efi_gpt_free_partition_content_cookie(partition_data *partition) { - delete (EFI::Header *)_cookie; + delete (EFI::Header *)partition->content_cookie; } +#ifndef _BOOT_MODE +static uint32 +efi_gpt_get_supported_operations(partition_data *partition, uint32 mask) +{ + uint32 flags = B_DISK_SYSTEM_SUPPORTS_INITIALIZING + | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME + | B_DISK_SYSTEM_SUPPORTS_MOVING + | B_DISK_SYSTEM_SUPPORTS_RESIZING + | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD; + // TODO: check for available entries and partitionable space and only + // add creating child support if both is valid + + return flags; +} + + +static uint32 +efi_gpt_get_supported_child_operations(partition_data *partition, + partition_data *child, uint32 mask) +{ + return B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD + | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD + | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE + | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD; +} + + +static bool +efi_gpt_is_sub_system_for(partition_data *partition) +{ + // a GUID Partition Table doesn't usually live inside another partition + return false; +} + + +static bool +efi_gpt_validate_resize(partition_data *partition, off_t *size) +{ + off_t newSize = *size; + if (newSize == partition->size) + return true; + + if (newSize < 0) + newSize = 0; + else + newSize = block_align(partition, newSize, false); + + // growing + if (newSize > partition->size) { + *size = newSize; + return true; + } + + // shrinking, only so that no child would be truncated + off_t newEnd = partition->offset + newSize; + for (int32 i = 0; i < partition->child_count; i++) { + partition_data *child = get_child_partition(partition->id, i); + if (child == NULL) + continue; + + if (child->offset + child->size > newEnd) + newEnd = child->offset + child->size; + } + + newSize = block_align(partition, newEnd - partition->offset, true); + *size = newSize; + return true; +} + + +static bool +efi_gpt_validate_resize_child(partition_data *partition, partition_data *child, + off_t *size) +{ + off_t newSize = *size; + if (newSize == child->size) + return true; + + // shrinking + if (newSize < child->size) { + if (newSize < 0) + newSize = 0; + + *size = block_align(partition, newSize, false); + return true; + } + + // growing, but only so much that the child doesn't get bigger than + // the parent + if (child->offset + newSize > partition->offset + partition->size) + newSize = partition->offset + partition->size - child->offset; + + // make sure that the child doesn't overlap any sibling partitions + off_t newEnd = child->offset + newSize; + for (int32 i = 0; i < partition->child_count; i++) { + partition_data *other = get_child_partition(partition->id, i); + if (other == NULL || other->id == child->id + || other->offset < child->offset) + continue; + + if (newEnd > other->offset) + newEnd = other->offset; + } + + *size = block_align(partition, newEnd - child->offset, false); + return true; +} + + +static bool +efi_gpt_validate_move(partition_data *partition, off_t *start) +{ + // nothing to do + return true; +} + + +static bool +efi_gpt_validate_move_child(partition_data *partition, partition_data *child, + off_t *start) +{ + off_t newStart = *start; + if (newStart < 0) + newStart = 0; + + if (newStart + child->size > partition->size) + newStart = partition->size - child->size; + + newStart = block_align(partition, newStart, false); + if (newStart > child->offset) { + for (int32 i = 0; i < partition->child_count; i++) { + partition_data *other = get_child_partition(partition->id, i); + if (other == NULL || other->id == child->id + || other->offset < child->offset) + continue; + + if (other->offset < newStart + child->size) + newStart = other->offset - child->size; + } + + newStart = block_align(partition, newStart, false); + } else { + for (int32 i = 0; i < partition->child_count; i++) { + partition_data *other = get_child_partition(partition->id, i); + if (other == NULL || other->id == child->id + || other->offset > child->offset) + continue; + + if (other->offset + other->size > newStart) + newStart = other->offset + other->size; + } + + newStart = block_align(partition, newStart, true); + } + + *start = newStart; + return true; +} + + +static bool +efi_gpt_validate_set_content_name(partition_data *partition, char *name) +{ + // TODO: should validate that the utf-8 -> ucs-2 is valid + // TODO: should count actual utf-8 chars + if (strlen(name) > EFI_PARTITION_NAME_LENGTH) + name[EFI_PARTITION_NAME_LENGTH - 1] = 0; + return true; +} + + +static bool +efi_gpt_validate_set_type(partition_data *partition, const char *type) +{ + return guid_for_partition_type(type) != NULL; +} + + +static bool +efi_gpt_validate_initialize(partition_data *partition, char *name, + const char *parameters) +{ + if ((efi_gpt_get_supported_operations(partition, ~0) + & B_DISK_SYSTEM_SUPPORTS_INITIALIZING) == 0) + return false; + + // name and parameters are ignored + if (name != NULL) + name[0] = 0; + + return true; +} + + +static bool +efi_gpt_validate_create_child(partition_data *partition, off_t *start, + off_t *size, const char *type, const char *parameters, int32 *index) +{ + if ((efi_gpt_get_supported_operations(partition, ~0) + & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) == 0) + return false; + + if (guid_for_partition_type(type) == NULL) + return false; + + EFI::Header *header = (EFI::Header *)partition->content_cookie; + int32 entryIndex = -1; + for (uint32 i = 0; i < header->EntryCount(); i++) { + const efi_partition_entry &entry = header->EntryAt(i); + if (entry.partition_type == kEmptyGUID) { + entryIndex = i; + break; + } + } + + if (entryIndex < 0) + return false; + *index = entryIndex; + + // ensure that child lies between first and last usable block + off_t firstUsable = header->FirstUsableBlock() * partition->block_size; + if (*start < firstUsable) + *start = firstUsable; + + off_t lastUsable = header->LastUsableBlock() * partition->block_size; + if (*start + *size > lastUsable) { + if (*start > lastUsable) + return false; + + *size = lastUsable - *start; + } + + // ensure that we don't overlap any siblings + for (int32 i = 0; i < partition->child_count; i++) { + partition_data *other = get_child_partition(partition->id, i); + if (other == NULL) + continue; + + if (other->offset < *start && other->offset + other->size > *start) + *start = other->offset + other->size; + + if (other->offset > *start && other->offset < *start + *size) + *size = other->offset - *start; + } + + *start = block_align(partition, *size, true); + *size = block_align(partition, *size, false); + + // TODO: support parameters + return true; +} + + +static status_t +efi_gpt_get_partitionable_spaces(partition_data *partition, + partitionable_space_data *buffer, int32 count, int32 *actualCount) +{ + // TODO: implement + return B_ERROR; +} + + +static status_t +efi_gpt_get_next_supported_type(partition_data *partition, int32 *cookie, + char *type) +{ + // TODO: implement + return B_ERROR; +} + + +static status_t +efi_gpt_shadow_changed(partition_data *partition, partition_data *child, + uint32 operation) +{ + // TODO: implement + return B_ERROR; +} + + +static status_t +efi_gpt_repair(int fd, partition_id partition, bool checkOnly, disk_job_id job) +{ + // TODO: implement, validate CRCs and restore from backup area if corrupt + return B_ERROR; +} + + +static status_t +efi_gpt_resize(int fd, partition_id partitionID, off_t size, disk_job_id job) +{ + if (fd < 0) + return B_ERROR; + + PartitionWriteLocker locker(partitionID); + if (!locker.IsLocked()) + return B_ERROR; + + partition_data *partition = get_partition(partitionID); + if (partition == NULL) + return B_BAD_VALUE; + + off_t validatedSize = size; + if (!efi_gpt_validate_resize(partition, &validatedSize)) + return B_BAD_VALUE; + + update_disk_device_job_progress(job, 0.0); + + partition->size = validatedSize; + partition->content_size = validatedSize; + + update_disk_device_job_progress(job, 1.0); + partition_modified(partitionID); + return B_OK; +} + + +static status_t +efi_gpt_resize_child(int fd, partition_id partitionID, off_t size, + disk_job_id job) +{ + if (fd < 0) + return B_ERROR; + + PartitionWriteLocker locker(partitionID); + if (!locker.IsLocked()) + return B_ERROR; + + partition_data *child = get_partition(partitionID); + if (child == NULL) + return B_BAD_VALUE; + + partition_data *partition = get_parent_partition(partitionID); + if (partition == NULL) + return B_BAD_VALUE; + + EFI::Header *header = (EFI::Header *)partition->content_cookie; + if (header == NULL) + return B_BAD_VALUE; + + uint32 entryIndex = (uint32)child->cookie; + if (entryIndex >= header->EntryCount()) + return B_BAD_VALUE; + + off_t validatedSize = size; + if (!efi_gpt_validate_resize_child(partition, child, &validatedSize)) + return B_BAD_VALUE; + + if (child->size == validatedSize) + return B_OK; + + update_disk_device_job_progress(job, 0.0); + + efi_partition_entry &entry = header->EntryAt(entryIndex); + entry.SetBlockCount(validatedSize / partition->block_size); + + status_t result = header->WriteEntry(fd, entryIndex); + if (result != B_OK) { + entry.SetBlockCount(child->size / partition->block_size); + return result; + } + + child->size = validatedSize; + + update_disk_device_job_progress(job, 1.0); + partition_modified(partitionID); + return B_OK; +} + + +static status_t +efi_gpt_move(int fd, partition_id partition, off_t offset, disk_job_id job) +{ + // nothing to do here + return B_OK; +} + + +static status_t +efi_gpt_move_child(int fd, partition_id partitionID, partition_id childID, + off_t offset, disk_job_id job) +{ + if (fd < 0) + return B_ERROR; + + PartitionWriteLocker locker(partitionID); + if (!locker.IsLocked()) + return B_ERROR; + + partition_data *partition = get_partition(partitionID); + if (partition == NULL) + return B_BAD_VALUE; + + partition_data *child = get_partition(childID); + if (child == NULL) + return B_BAD_VALUE; + + EFI::Header *header = (EFI::Header *)partition->content_cookie; + if (header == NULL) + return B_BAD_VALUE; + + uint32 entryIndex = (uint32)child->cookie; + if (entryIndex >= header->EntryCount()) + return B_BAD_VALUE; + + off_t validatedOffset = offset; + if (!efi_gpt_validate_move_child(partition, child, &validatedOffset)) + return B_BAD_VALUE; + + if (child->offset == validatedOffset) + return B_OK; + + // TODO: implement actual moving, need to move the partition content + // (the raw data) here and need to take overlap into account + return B_ERROR; + + update_disk_device_job_progress(job, 0.0); + + efi_partition_entry &entry = header->EntryAt(entryIndex); + uint64 blockCount = entry.BlockCount(); + entry.SetStartBlock((validatedOffset - partition->offset) + / partition->block_size); + entry.SetBlockCount(blockCount); + + status_t result = header->WriteEntry(fd, entryIndex); + if (result != B_OK) { + // fatal error: the data has been moved but the partition table could + // not be updated to reflect that change! + return result; + } + + child->offset = validatedOffset; + + update_disk_device_job_progress(job, 1.0); + partition_modified(childID); + return B_OK; +} + + +static status_t +efi_gpt_set_content_name(int fd, partition_id partitionID, const char *name, + disk_job_id job) +{ + if (fd < 0) + return B_ERROR; + + PartitionWriteLocker locker(partitionID); + if (!locker.IsLocked()) + return B_ERROR; + + partition_data *child = get_partition(partitionID); + if (child == NULL) + return B_BAD_VALUE; + + partition_data *partition = get_parent_partition(partitionID); + if (partition == NULL) + return B_BAD_VALUE; + + EFI::Header *header = (EFI::Header *)partition->content_cookie; + if (header == NULL) + return B_BAD_VALUE; + + uint32 entryIndex = (uint32)child->cookie; + if (entryIndex >= header->EntryCount()) + return B_BAD_VALUE; + + update_disk_device_job_progress(job, 0.0); + + efi_partition_entry &entry = header->EntryAt(entryIndex); + to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH); + + status_t result = header->WriteEntry(fd, entryIndex); + if (result != B_OK) + return result; + + char newName[B_OS_NAME_LENGTH]; + to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, newName, sizeof(newName)); + child->name = strdup(newName); + + update_disk_device_job_progress(job, 1.0); + partition_modified(partitionID); + return B_OK; +} + + +static status_t +efi_gpt_set_type(int fd, partition_id partitionID, const char *type, + disk_job_id job) +{ + if (fd < 0) + return B_ERROR; + + PartitionWriteLocker locker(partitionID); + if (!locker.IsLocked()) + return B_ERROR; + + partition_data *child = get_partition(partitionID); + if (child == NULL) + return B_BAD_VALUE; + + partition_data *partition = get_parent_partition(partitionID); + if (partition == NULL) + return B_BAD_VALUE; + + EFI::Header *header = (EFI::Header *)partition->content_cookie; + if (header == NULL) + return B_BAD_VALUE; + + uint32 entryIndex = (uint32)child->cookie; + if (entryIndex >= header->EntryCount()) + return B_BAD_VALUE; + + const static_guid *newType = guid_for_partition_type(type); + if (newType == NULL) + return B_BAD_VALUE; + + update_disk_device_job_progress(job, 0.0); + + efi_partition_entry &entry = header->EntryAt(entryIndex); + memcpy(&entry.partition_type, newType, sizeof(entry.partition_type)); + + status_t result = header->WriteEntry(fd, entryIndex); + if (result != B_OK) + return result; + + child->type = strdup(type); + + update_disk_device_job_progress(job, 1.0); + partition_modified(partitionID); + return B_OK; +} + + +static status_t +efi_gpt_initialize(int fd, partition_id partitionID, const char *name, + const char *parameters, off_t partitionSize, disk_job_id job) +{ + if (fd < 0) + return B_ERROR; + + partition_data *partition = get_partition(partitionID); + if (partition == NULL) + return B_BAD_VALUE; + + update_disk_device_job_progress(job, 0.0); + + EFI::Header header(EFI_HEADER_LOCATION, partition->block_size); + status_t result = header.InitCheck(); + if (result != B_OK) + return result; + + result = header.Write(fd); + if (result != B_OK) + return result; + + result = scan_partition(partitionID); + if (result != B_OK) + return result; + + update_disk_device_job_progress(job, 1.0); + partition_modified(partitionID); + return B_OK; +} + + +static status_t +efi_gpt_create_child(int fd, partition_id partitionID, off_t offset, + off_t size, const char *type, const char *parameters, disk_job_id job, + partition_id *childID) +{ + if (fd < 0) + return B_ERROR; + + PartitionWriteLocker locker(partitionID); + if (!locker.IsLocked()) + return B_ERROR; + + partition_data *partition = get_partition(partitionID); + if (partition == NULL) + return B_BAD_VALUE; + + EFI::Header *header = (EFI::Header *)partition->content_cookie; + if (header == NULL) + return B_BAD_VALUE; + + off_t validatedOffset = offset; + off_t validatedSize = size; + uint32 entryIndex = 0; + + if (!efi_gpt_validate_create_child(partition, &validatedOffset, + &validatedSize, type, parameters, (int32 *)&entryIndex)) + return B_BAD_VALUE; + + const static_guid *newType = guid_for_partition_type(type); + if (newType == NULL) + return B_BAD_VALUE; + + update_disk_device_job_progress(job, 0.0); + + partition_data *child = create_child_partition(partition->id, entryIndex, + *childID); + if (child == NULL) + return B_ERROR; + + efi_partition_entry &entry = header->EntryAt(entryIndex); + memcpy(&entry.partition_type, newType, sizeof(entry.partition_type)); + entry.SetStartBlock((validatedOffset - partition->offset) + / partition->block_size); + entry.SetBlockCount(validatedSize / partition->block_size); + entry.SetAttributes(0); // TODO + + status_t result = header->WriteEntry(fd, entryIndex); + if (result != B_OK) { + delete_partition(child->id); + return result; + } + + *childID = child->id; + child->offset = validatedOffset; + child->size = validatedSize; + child->block_size = partition->block_size; + child->type = strdup(type); + child->parameters = strdup(parameters); + child->cookie = (void *)entryIndex; + + if (child->type == NULL || child->parameters == NULL) { + delete_partition(child->id); + return B_NO_MEMORY; + } + + update_disk_device_job_progress(job, 1.0); + partition_modified(partitionID); + return B_OK; +} + + +static status_t +efi_gpt_delete_child(int fd, partition_id partitionID, partition_id childID, + disk_job_id job) +{ + if (fd < 0) + return B_ERROR; + + PartitionWriteLocker locker(partitionID); + if (!locker.IsLocked()) + return B_ERROR; + + partition_data *partition = get_partition(partitionID); + if (partition == NULL) + return B_BAD_VALUE; + + partition_data *child = get_partition(childID); + if (child == NULL) + return B_BAD_VALUE; + + EFI::Header *header = (EFI::Header *)partition->content_cookie; + if (header == NULL) + return B_BAD_VALUE; + + uint32 entryIndex = (uint32)child->cookie; + if (entryIndex >= header->EntryCount()) + return B_BAD_VALUE; + + update_disk_device_job_progress(job, 0.0); + + if (!delete_partition(childID)) + return B_ERROR; + + efi_partition_entry &entry = header->EntryAt(entryIndex); + entry.partition_type = kEmptyGUID; + + status_t result = header->WriteEntry(fd, entryIndex); + if (result != B_OK) + return result; + + update_disk_device_job_progress(job, 1.0); + partition_modified(partitionID); + return B_OK; +} +#endif // !_BOOT_MODE + + #ifndef _BOOT_MODE static partition_module_info sEFIPartitionModule = { #else @@ -409,13 +1206,68 @@ partition_module_info gEFIPartitionModule = { }, "efi", // short_name EFI_PARTITION_NAME, // pretty_name - 0, // flags + 0 // flags + | B_DISK_SYSTEM_SUPPORTS_INITIALIZING + | B_DISK_SYSTEM_SUPPORTS_MOVING + | B_DISK_SYSTEM_SUPPORTS_RESIZING + | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE + | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME + | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME + | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD + | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD + | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD + | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD + , // scanning efi_gpt_identify_partition, efi_gpt_scan_partition, - efi_gpt_free_identify_partition_cookie, - NULL, + NULL, // free_identify_partition_cookie + NULL, // free_partition_cookie + efi_gpt_free_partition_content_cookie, + +#ifndef _BOOT_MODE + // querying + efi_gpt_get_supported_operations, + efi_gpt_get_supported_child_operations, + NULL, // supports_initializing_child + efi_gpt_is_sub_system_for, + + efi_gpt_validate_resize, + efi_gpt_validate_resize_child, + efi_gpt_validate_move, + efi_gpt_validate_move_child, + NULL, // validate_set_name + efi_gpt_validate_set_content_name, + efi_gpt_validate_set_type, + NULL, // validate_set_parameters + NULL, // validate_set_content_parameters + efi_gpt_validate_initialize, + efi_gpt_validate_create_child, + efi_gpt_get_partitionable_spaces, + efi_gpt_get_next_supported_type, + NULL, // get_type_for_content_type + + // shadow partition modification + efi_gpt_shadow_changed, + + // writing + efi_gpt_repair, + efi_gpt_resize, + efi_gpt_resize_child, + efi_gpt_move, + efi_gpt_move_child, + NULL, // set_name + efi_gpt_set_content_name, + efi_gpt_set_type, + NULL, // set_parameters + NULL, // set_content_parameters + efi_gpt_initialize, + efi_gpt_create_child, + efi_gpt_delete_child +#else + NULL +#endif // _BOOT_MODE }; #ifndef _BOOT_MODE diff --git a/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.h b/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.h index 33f9f1a38c..4436362b47 100644 --- a/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.h +++ b/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.h @@ -31,36 +31,62 @@ struct efi_table_header { // the rest of the block is reserved + void SetRevision(uint32 revision) + { revision = B_HOST_TO_LENDIAN_INT32(revision); } uint32 Revision() const { return B_LENDIAN_TO_HOST_INT32(revision); } + void SetHeaderSize(uint32 size) + { header_size = B_HOST_TO_LENDIAN_INT32(size); } uint32 HeaderSize() const { return B_LENDIAN_TO_HOST_INT32(header_size); } + void SetHeaderCRC(uint32 crc) + { header_crc = B_HOST_TO_LENDIAN_INT32(crc); } uint32 HeaderCRC() const { return B_LENDIAN_TO_HOST_INT32(header_crc); } + void SetAbsoluteBlock(uint64 block) + { absolute_block = B_HOST_TO_LENDIAN_INT64(block); } uint64 AbsoluteBlock() const { return B_LENDIAN_TO_HOST_INT64(absolute_block); } + void SetAlternateBlock(uint64 block) + { alternate_block = B_HOST_TO_LENDIAN_INT64(block); } uint64 AlternateBlock() const { return B_LENDIAN_TO_HOST_INT64(alternate_block); } + void SetFirstUsableBlock(uint64 block) + { first_usable_block = B_HOST_TO_LENDIAN_INT64(block); } uint64 FirstUsableBlock() const { return B_LENDIAN_TO_HOST_INT64(first_usable_block); } + void SetLastUsableBlock(uint64 block) + { last_usable_block = B_HOST_TO_LENDIAN_INT64(block); } uint64 LastUsableBlock() const { return B_LENDIAN_TO_HOST_INT64(last_usable_block); } + void SetEntriesBlock(uint64 block) + { entries_block = B_HOST_TO_LENDIAN_INT64(block); } uint64 EntriesBlock() const { return B_LENDIAN_TO_HOST_INT64(entries_block); } + void SetEntryCount(uint32 count) + { entry_count = B_HOST_TO_LENDIAN_INT32(count); } uint32 EntryCount() const { return B_LENDIAN_TO_HOST_INT32(entry_count); } + void SetEntrySize(uint32 size) + { entry_size = B_HOST_TO_LENDIAN_INT32(size); } uint32 EntrySize() const { return B_LENDIAN_TO_HOST_INT32(entry_size); } + void SetEntriesCRC(uint32 crc) + { entries_crc = B_HOST_TO_LENDIAN_INT32(crc); } uint32 EntriesCRC() const { return B_LENDIAN_TO_HOST_INT32(entries_crc); } } _PACKED; -#define EFI_PARTITION_HEADER "EFI PART" -#define EFI_HEADER_LOCATION 1 +#define EFI_PARTITION_HEADER "EFI PART" +#define EFI_HEADER_LOCATION 1 +#define EFI_TABLE_REVISION 0x00010000 -#define EFI_PARTITION_NAME_LENGTH 36 +#define EFI_PARTITION_NAME_LENGTH 36 +#define EFI_PARTITION_ENTRIES_BLOCK 2 +#define EFI_PARTITION_ENTRY_COUNT 128 +#define EFI_PARTITION_ENTRY_SIZE 128 struct efi_partition_entry { guid_t partition_type; @@ -70,12 +96,24 @@ struct efi_partition_entry { uint64 attributes; uint16 name[EFI_PARTITION_NAME_LENGTH]; + void SetStartBlock(uint64 block) + { start_block = B_HOST_TO_LENDIAN_INT64(block); } uint64 StartBlock() const { return B_LENDIAN_TO_HOST_INT64(start_block); } + void SetEndBlock(uint64 block) + { end_block = B_HOST_TO_LENDIAN_INT64(block); } uint64 EndBlock() const { return B_LENDIAN_TO_HOST_INT64(end_block); } + void SetAttributes(uint64 _attributes) + { attributes = B_HOST_TO_LENDIAN_INT64(_attributes); } uint64 Attributes() const { return B_LENDIAN_TO_HOST_INT64(attributes); } + + // convenience functions + void SetBlockCount(uint64 blockCount) + { SetEndBlock(StartBlock() + blockCount - 1); } + uint64 BlockCount() const + { return EndBlock() - StartBlock(); } } _PACKED; #endif /* EFI_GPT_H */