diff options
author | Liav A <liavalb@gmail.com> | 2020-12-26 16:53:30 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-12-27 23:07:44 +0100 |
commit | 72b1998f0d228f63cd5b095b282ee534f285bcb2 (patch) | |
tree | 742ae0ac006f1309ea3ba12c76b09b598e139dda | |
parent | bc18712adf8b683d3f8a319df3fd1ea5cf03b517 (diff) | |
download | serenity-72b1998f0d228f63cd5b095b282ee534f285bcb2.zip |
Kernel: Introduce a new partitioning subsystem
The partitioning code was very outdated, and required a full refactor.
The new subsystem removes duplicated code and uses more AK containers.
The most important change is that all implementations of the
PartitionTable class conform to one interface, which made it possible
to remove unnecessary code in the EBRPartitionTable class.
Finding partitions is now done in the StorageManagement singleton,
instead of doing so in init.cpp.
Also, now we don't try to find partitions on demand - the kernel will
try to detect if a StorageDevice is partitioned, and if so, will check
what is the partition table, which could be MBR, GUID or EBR.
Then, it will create DiskPartitionMetadata object for each partition
that is available in the partition table. This object will be used
by the partition enumeration code to create a DiskPartition with the
correct minor number.
20 files changed, 624 insertions, 546 deletions
diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 373d0ebfb2..46e55fe30d 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -38,8 +38,9 @@ set(KERNEL_SOURCES Storage/Partition/DiskPartition.cpp Storage/Partition/DiskPartitionMetadata.cpp Storage/Partition/EBRPartitionTable.cpp - Storage/Partition/GPTPartitionTable.cpp + Storage/Partition/GUIDPartitionTable.cpp Storage/Partition/MBRPartitionTable.cpp + Storage/Partition/PartitionTable.cpp Storage/StorageDevice.cpp Storage/IDEController.cpp Storage/IDEChannel.cpp diff --git a/Kernel/FileSystem/DevFS.cpp b/Kernel/FileSystem/DevFS.cpp index f86fda89e9..127a67de90 100644 --- a/Kernel/FileSystem/DevFS.cpp +++ b/Kernel/FileSystem/DevFS.cpp @@ -411,7 +411,10 @@ String DevFSDeviceInode::determine_name() const } case 100: - return "hda1"; + // FIXME: Try to not hardcode a maximum of 16 partitions per drive! + size_t drive_index = (u8)'a' + (m_attached_device->minor() / 16); + char drive_letter = (u8)drive_index; + return String::format("hd%c%d", drive_letter, m_attached_device->minor() + 1); } } diff --git a/Kernel/Storage/Partition/DiskPartition.cpp b/Kernel/Storage/Partition/DiskPartition.cpp index 64126c0b58..50b97c276d 100644 --- a/Kernel/Storage/Partition/DiskPartition.cpp +++ b/Kernel/Storage/Partition/DiskPartition.cpp @@ -31,16 +31,15 @@ namespace Kernel { -NonnullRefPtr<DiskPartition> DiskPartition::create(BlockDevice& device, unsigned block_offset, unsigned block_limit) +NonnullRefPtr<DiskPartition> DiskPartition::create(BlockDevice& device, unsigned minor_number, DiskPartitionMetadata metadata) { - return adopt(*new DiskPartition(device, block_offset, block_limit)); + return adopt(*new DiskPartition(device, minor_number, metadata)); } -DiskPartition::DiskPartition(BlockDevice& device, unsigned block_offset, unsigned block_limit) - : BlockDevice(100, 0, device.block_size()) +DiskPartition::DiskPartition(BlockDevice& device, unsigned minor_number, DiskPartitionMetadata metadata) + : BlockDevice(100, minor_number, device.block_size()) , m_device(device) - , m_block_offset(block_offset) - , m_block_limit(block_limit) + , m_metadata(metadata) { } @@ -51,12 +50,12 @@ DiskPartition::~DiskPartition() void DiskPartition::start_request(AsyncBlockDeviceRequest& request) { request.add_sub_request(m_device->make_request<AsyncBlockDeviceRequest>(request.request_type(), - request.block_index() + m_block_offset, request.block_count(), request.buffer(), request.buffer_size())); + request.block_index() + m_metadata.start_block(), request.block_count(), request.buffer(), request.buffer_size())); } KResultOr<size_t> DiskPartition::read(FileDescription& fd, size_t offset, UserOrKernelBuffer& outbuf, size_t len) { - unsigned adjust = m_block_offset * block_size(); + unsigned adjust = m_metadata.start_block() * block_size(); #ifdef OFFD_DEBUG klog() << "DiskPartition::read offset=" << fd.offset() << " adjust=" << adjust << " len=" << len; @@ -67,7 +66,7 @@ KResultOr<size_t> DiskPartition::read(FileDescription& fd, size_t offset, UserOr bool DiskPartition::can_read(const FileDescription& fd, size_t offset) const { - unsigned adjust = m_block_offset * block_size(); + unsigned adjust = m_metadata.start_block() * block_size(); #ifdef OFFD_DEBUG klog() << "DiskPartition::can_read offset=" << offset << " adjust=" << adjust; @@ -78,7 +77,7 @@ bool DiskPartition::can_read(const FileDescription& fd, size_t offset) const KResultOr<size_t> DiskPartition::write(FileDescription& fd, size_t offset, const UserOrKernelBuffer& inbuf, size_t len) { - unsigned adjust = m_block_offset * block_size(); + unsigned adjust = m_metadata.start_block() * block_size(); #ifdef OFFD_DEBUG klog() << "DiskPartition::write offset=" << offset << " adjust=" << adjust << " len=" << len; @@ -89,7 +88,7 @@ KResultOr<size_t> DiskPartition::write(FileDescription& fd, size_t offset, const bool DiskPartition::can_write(const FileDescription& fd, size_t offset) const { - unsigned adjust = m_block_offset * block_size(); + unsigned adjust = m_metadata.start_block() * block_size(); #ifdef OFFD_DEBUG klog() << "DiskPartition::can_write offset=" << offset << " adjust=" << adjust; diff --git a/Kernel/Storage/Partition/DiskPartition.h b/Kernel/Storage/Partition/DiskPartition.h index acd9547c9a..87409a3e26 100644 --- a/Kernel/Storage/Partition/DiskPartition.h +++ b/Kernel/Storage/Partition/DiskPartition.h @@ -28,12 +28,13 @@ #include <AK/RefPtr.h> #include <Kernel/Devices/BlockDevice.h> +#include <Kernel/Storage/Partition/DiskPartitionMetadata.h> namespace Kernel { class DiskPartition final : public BlockDevice { public: - static NonnullRefPtr<DiskPartition> create(BlockDevice&, unsigned block_offset, unsigned block_limit); + static NonnullRefPtr<DiskPartition> create(BlockDevice&, unsigned, DiskPartitionMetadata); virtual ~DiskPartition(); virtual void start_request(AsyncBlockDeviceRequest&) override; @@ -50,11 +51,10 @@ public: private: virtual const char* class_name() const override; - DiskPartition(BlockDevice&, unsigned block_offset, unsigned block_limit); + DiskPartition(BlockDevice&, unsigned, DiskPartitionMetadata); NonnullRefPtr<BlockDevice> m_device; - unsigned m_block_offset; - unsigned m_block_limit; + DiskPartitionMetadata m_metadata; }; } diff --git a/Kernel/Storage/Partition/DiskPartitionMetadata.cpp b/Kernel/Storage/Partition/DiskPartitionMetadata.cpp index 99c5a17d21..82ded057e9 100644 --- a/Kernel/Storage/Partition/DiskPartitionMetadata.cpp +++ b/Kernel/Storage/Partition/DiskPartitionMetadata.cpp @@ -45,6 +45,12 @@ DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, Byt ASSERT(!m_partition_type.is_empty()); ASSERT(!m_unique_guid.is_empty()); } + +DiskPartitionMetadata DiskPartitionMetadata::offset(u64 blocks_count) const +{ + return DiskPartitionMetadata({ blocks_count + m_start_block, blocks_count + m_end_block, m_partition_type }); +} + u64 DiskPartitionMetadata::start_block() const { return m_start_block; diff --git a/Kernel/Storage/Partition/DiskPartitionMetadata.h b/Kernel/Storage/Partition/DiskPartitionMetadata.h index b1c551ced9..5c1eae70e5 100644 --- a/Kernel/Storage/Partition/DiskPartitionMetadata.h +++ b/Kernel/Storage/Partition/DiskPartitionMetadata.h @@ -38,6 +38,8 @@ public: u64 start_block() const; u64 end_block() const; + DiskPartitionMetadata offset(u64 blocks_count) const; + Optional<u64> special_attributes() const; Optional<String> name() const; Optional<ByteBuffer> partition_type() const; @@ -48,7 +50,7 @@ private: u64 m_end_block; ByteBuffer m_partition_type; ByteBuffer m_unique_guid; - u64 m_attributes; + u64 m_attributes { 0 }; String m_name; }; diff --git a/Kernel/Storage/Partition/EBRPartitionTable.cpp b/Kernel/Storage/Partition/EBRPartitionTable.cpp index e026ea3576..e99e90f3bc 100644 --- a/Kernel/Storage/Partition/EBRPartitionTable.cpp +++ b/Kernel/Storage/Partition/EBRPartitionTable.cpp @@ -33,176 +33,69 @@ namespace Kernel { -EBRPartitionTable::EBRPartitionTable(NonnullRefPtr<BlockDevice> device) - : m_device(move(device)) +Result<NonnullOwnPtr<EBRPartitionTable>, PartitionTable::Error> EBRPartitionTable::try_to_initialize(const StorageDevice& device) { + auto table = make<EBRPartitionTable>(device); + if (table->is_protective_mbr()) + return { PartitionTable::Error::MBRProtective }; + if (!table->is_valid()) + return { PartitionTable::Error::Invalid }; + return table; } -EBRPartitionTable::~EBRPartitionTable() +void EBRPartitionTable::search_extended_partition(const StorageDevice& device, MBRPartitionTable& checked_ebr, u64 current_block_offset, size_t limit) { + if (limit == 0) + return; + // EBRs should not carry more than 2 partitions (because they need to form a linked list) + ASSERT(checked_ebr.partitions_count() <= 2); + auto checked_logical_partition = checked_ebr.partition(0); + + // If we are pointed to an invalid logical partition, something is seriously wrong. + ASSERT(checked_logical_partition.has_value()); + m_partitions.append(checked_logical_partition.value().offset(current_block_offset)); + if (!checked_ebr.contains_ebr()) + return; + current_block_offset += checked_ebr.partition(1).value().start_block(); + auto next_ebr = MBRPartitionTable::try_to_initialize(device, current_block_offset); + if (!next_ebr) + return; + search_extended_partition(device, *next_ebr, current_block_offset, (limit - 1)); } -const MBRPartitionHeader& EBRPartitionTable::header() const +EBRPartitionTable::EBRPartitionTable(const StorageDevice& device) + : MBRPartitionTable(device) { - return *reinterpret_cast<const MBRPartitionHeader*>(m_cached_mbr_header); -} + if (!is_header_valid()) + return; + m_valid = true; -const EBRPartitionExtension& EBRPartitionTable::ebr_extension() const -{ - return *reinterpret_cast<const EBRPartitionExtension*>(m_cached_ebr_header); -} + ASSERT(partitions_count() == 0); -int EBRPartitionTable::index_of_ebr_container() const -{ - for (int i = 0; i < 4; i++) { - if (header().entry[i].type == EBR_CHS_CONTAINER || header().entry[i].type == EBR_LBA_CONTAINER) - return i; - } - ASSERT_NOT_REACHED(); -} - -bool EBRPartitionTable::initialize() -{ - auto mbr_header_request = m_device->make_request<AsyncBlockDeviceRequest>(AsyncBlockDeviceRequest::Read, - 0, 1, UserOrKernelBuffer::for_kernel_buffer(m_cached_mbr_header), sizeof(m_cached_mbr_header)); - - auto mbr_header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_mbr_header); - if (!m_device->read_block(0, mbr_header_buffer)) { - return false; - } auto& header = this->header(); - - m_ebr_container_id = index_of_ebr_container() + 1; - -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::initialize: MBR_signature=0x" << String::format("%x", header.mbr_signature); -#endif - - if (header.mbr_signature != MBR_SIGNATURE) { - klog() << "EBRPartitionTable::initialize: bad MBR signature 0x" << String::format("%x", header.mbr_signature); - return false; - } - - auto& ebr_entry = header.entry[m_ebr_container_id - 1]; - auto ebr_header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_ebr_header); - if (!m_device->read_block(ebr_entry.offset, ebr_header_buffer)) { - return false; - } - size_t index = 1; - while (index < 128) { // Unlikely to encounter a disk with 128 partitions in this configuration... - if (ebr_extension().next_chained_ebr_extension.offset == 0 && ebr_extension().next_chained_ebr_extension.type == 0) { - break; - } - index++; - if (!m_device->read_block(ebr_extension().next_chained_ebr_extension.offset, ebr_header_buffer)) { - return false; + for (size_t index = 0; index < 4; index++) { + auto& entry = header.entry[index]; + // Start enumerating all logical partitions + if (entry.type == 0xf) { + auto checked_ebr = MBRPartitionTable::try_to_initialize(device, entry.offset); + if (!checked_ebr) + continue; + // It's quite unlikely to see that amount of partitions, so stop at 128 partitions. + search_extended_partition(device, *checked_ebr, entry.offset, 128); + continue; } - } - - m_ebr_chained_extensions_count = index; - - klog() << "EBRPartitionTable::initialize: Extended partitions count - " << m_ebr_chained_extensions_count; - - return true; -} - -RefPtr<DiskPartition> EBRPartitionTable::get_non_extended_partition(unsigned index) -{ - auto& header = this->header(); - auto& entry = header.entry[index - 1]; - -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: status=0x" << String::format("%x", entry.status) << " offset=0x" << String::format("%x", entry.offset); -#endif - - if (entry.offset == 0x00) { -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: missing partition requested index=" << index; -#endif - - return nullptr; - } -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: found partition index=" << index << " type=" << String::format("%x", entry.type); -#endif - - return DiskPartition::create(m_device, entry.offset, (entry.offset + entry.length)); -} - -RefPtr<DiskPartition> EBRPartitionTable::get_extended_partition(unsigned index) -{ - - unsigned relative_index = index - m_ebr_container_id; - auto& header = this->header(); - -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: relative index " << relative_index; -#endif - - auto& ebr_entry = header.entry[m_ebr_container_id - 1]; -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: Extended partition, offset 0x" << String::format("%x", ebr_entry.offset) << ", type " << String::format("%x", ebr_entry.type); -#endif - - auto ebr_header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_ebr_header); - if (!m_device->read_block(ebr_entry.offset, ebr_header_buffer)) { - return nullptr; - } - size_t i = 0; - while (i < relative_index) { -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: logical partition, relative offset 0x" << String::format("%x", ebr_extension().entry.offset) << ", type " << String::format("%x", ebr_extension().entry.type); - klog() << "EBRPartitionTable::partition: next logical partition, relative offset 0x" << String::format("%x", ebr_extension().next_chained_ebr_extension.offset) << ", type " << String::format("%x", ebr_extension().next_chained_ebr_extension.type); -#endif - if (ebr_extension().next_chained_ebr_extension.offset == 0 && ebr_extension().next_chained_ebr_extension.type == 0) { - break; - } - - i++; - if (!m_device->read_block(ebr_extension().next_chained_ebr_extension.offset, ebr_header_buffer)) { - return nullptr; + if (entry.offset == 0x00) { + continue; } + auto partition_type = ByteBuffer::create_zeroed(sizeof(u8)); + partition_type.data()[0] = entry.type; + m_partitions.append(DiskPartitionMetadata({ entry.offset, (entry.offset + entry.length), partition_type })); } - -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: status=" << String::format("%x", ebr_extension().entry.status) << " offset=" << String::format("%x", ebr_extension().entry.offset + ebr_entry.offset); -#endif - - if (ebr_extension().entry.offset == 0x00) { -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: missing partition requested index=" << index; -#endif - - return nullptr; - } - -#ifdef EBR_DEBUG - klog() << "EBRPartitionTable::partition: found partition index=" << index << " type=" << String::format("%x", ebr_extension().entry.type); -#endif - - return DiskPartition::create(m_device, ebr_extension().entry.offset + ebr_entry.offset, (ebr_extension().entry.offset + ebr_entry.offset + ebr_extension().entry.length)); -} - -bool EBRPartitionTable::index_is_extended_partition(unsigned index) const -{ - return !(m_ebr_container_id > index || index > (m_ebr_container_id + m_ebr_chained_extensions_count)); } -RefPtr<DiskPartition> EBRPartitionTable::partition(unsigned index) +EBRPartitionTable::~EBRPartitionTable() { - ASSERT(index >= 1 && index <= m_ebr_chained_extensions_count + 4); - - auto& header = this->header(); - if (header.mbr_signature != MBR_SIGNATURE) { - klog() << "EBRPartitionTable::initialize: bad MBR signature - not initialized? 0x" << String::format("%x", header.mbr_signature); - return nullptr; - } - if (index_is_extended_partition(index)) - return get_extended_partition(index); - if (index > 4) - return get_non_extended_partition(index - m_ebr_chained_extensions_count); - return get_non_extended_partition(index); } } diff --git a/Kernel/Storage/Partition/EBRPartitionTable.h b/Kernel/Storage/Partition/EBRPartitionTable.h index a5dab9660a..b9784706d4 100644 --- a/Kernel/Storage/Partition/EBRPartitionTable.h +++ b/Kernel/Storage/Partition/EBRPartitionTable.h @@ -26,46 +26,29 @@ #pragma once +#include <AK/NonnullOwnPtr.h> #include <AK/RefPtr.h> +#include <AK/Result.h> #include <AK/Vector.h> #include <Kernel/Storage/Partition/DiskPartition.h> #include <Kernel/Storage/Partition/MBRPartitionTable.h> namespace Kernel { -struct [[gnu::packed]] EBRPartitionExtension -{ - u8 unused_area[446]; - MBRPartitionEntry entry; - MBRPartitionEntry next_chained_ebr_extension; - MBRPartitionEntry unused[2]; - u16 mbr_signature; -}; - -class EBRPartitionTable { - +struct EBRPartitionHeader; +class EBRPartitionTable : public MBRPartitionTable { public: - explicit EBRPartitionTable(NonnullRefPtr<BlockDevice>); ~EBRPartitionTable(); - bool initialize(); - RefPtr<DiskPartition> partition(unsigned index); + static Result<NonnullOwnPtr<EBRPartitionTable>, PartitionTable::Error> try_to_initialize(const StorageDevice&); + explicit EBRPartitionTable(const StorageDevice&); + virtual bool is_valid() const override { return m_valid; }; + virtual Type type() const override { return Type::EBR; }; private: - int index_of_ebr_container() const; - NonnullRefPtr<BlockDevice> m_device; + void search_extended_partition(const StorageDevice&, MBRPartitionTable&, u64, size_t limit); - const MBRPartitionHeader& header() const; - const EBRPartitionExtension& ebr_extension() const; - - bool index_is_extended_partition(unsigned index) const; - - RefPtr<DiskPartition> get_extended_partition(unsigned index); - RefPtr<DiskPartition> get_non_extended_partition(unsigned index); - u8 m_ebr_container_id { 0 }; - size_t m_ebr_chained_extensions_count { 0 }; - u8 m_cached_mbr_header[512]; - u8 m_cached_ebr_header[512]; + bool m_valid { false }; + size_t m_partitions_count { 0 }; }; - } diff --git a/Kernel/Storage/Partition/GPTPartitionTable.cpp b/Kernel/Storage/Partition/GPTPartitionTable.cpp deleted file mode 100644 index af81a1ae4e..0000000000 --- a/Kernel/Storage/Partition/GPTPartitionTable.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/ByteBuffer.h> -#include <Kernel/Storage/Partition/GPTPartitionTable.h> - -#ifndef GPT_DEBUG -# define GPT_DEBUG -#endif - -namespace Kernel { - -GPTPartitionTable::GPTPartitionTable(BlockDevice& device) - : m_device(move(device)) -{ -} - -GPTPartitionTable::~GPTPartitionTable() -{ -} - -const GPTPartitionHeader& GPTPartitionTable::header() const -{ - return *reinterpret_cast<const GPTPartitionHeader*>(m_cached_header); -} - -bool GPTPartitionTable::initialize() -{ - auto header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_header); - if (!m_device->read_block(1, header_buffer)) { - return false; - } - - auto& header = this->header(); - -#ifdef GPT_DEBUG - klog() << "GPTPartitionTable::initialize: gpt_signature=0x" << String::format("%x", header.sig[1]) << String::format("%x", header.sig[0]); -#endif - - if (header.sig[0] != GPT_SIGNATURE && header.sig[1] != GPT_SIGNATURE2) { - klog() << "GPTPartitionTable::initialize: bad GPT signature 0x" << String::format("%x", header.sig[1]) << String::format("%x", header.sig[0]); - return false; - } - - return true; -} - -RefPtr<DiskPartition> GPTPartitionTable::partition(unsigned index) -{ - ASSERT(index >= 1 && index <= 4294967294); - - auto& header = this->header(); - unsigned lba = header.partition_array_start_lba + (((index - 1) * header.partition_entry_size) / BytesPerSector); - - if (header.sig[0] != GPT_SIGNATURE) { - klog() << "GPTPartitionTable::initialize: bad gpt signature - not initialized? 0x" << String::format("%x", header.sig); - return nullptr; - } - - u8 entries_per_sector = BytesPerSector / header.partition_entry_size; - - GPTPartitionEntry entries[entries_per_sector]; - auto entries_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&entries); - this->m_device->read_block(lba, entries_buffer); - GPTPartitionEntry& entry = entries[((index - 1) % entries_per_sector)]; - -#ifdef GPT_DEBUG - klog() << "GPTPartitionTable::partition " << index; - klog() << "GPTPartitionTable - offset = " << entry.first_lba[1] << entry.first_lba[0]; -#endif - - if (entry.first_lba[0] == 0x00) { -#ifdef GPT_DEBUG - klog() << "GPTPartitionTable::partition: missing partition requested index=" << index; -#endif - - return nullptr; - } - -#ifdef GPT_DEBUG - klog() << "GPTPartitionTable::partition: found partition index=" << index << " type=" << String::format("%x", entry.partition_guid[3]) << "-" << String::format("%x", entry.partition_guid[2]) << "-" << String::format("%x", entry.partition_guid[1]) << "-" << String::format("%x", entry.partition_guid[0]); -#endif - return DiskPartition::create(m_device, entry.first_lba[0], entry.last_lba[0]); -} - -} diff --git a/Kernel/Storage/Partition/GUIDPartitionTable.cpp b/Kernel/Storage/Partition/GUIDPartitionTable.cpp new file mode 100644 index 0000000000..f6064ff23a --- /dev/null +++ b/Kernel/Storage/Partition/GUIDPartitionTable.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <AK/ByteBuffer.h> +#include <Kernel/Storage/Partition/GUIDPartitionTable.h> + +#ifndef GPT_DEBUG +# define GPT_DEBUG +#endif + +namespace Kernel { + +#define GPT_SIGNATURE2 0x54524150 +#define GPT_SIGNATURE 0x20494645 +#define BytesPerSector 512 + +struct [[gnu::packed]] GPTPartitionEntry +{ + u8 partition_guid[16]; + u8 unique_guid[16]; + + u64 first_lba; + u64 last_lba; + + u64 attributes; + char partition_name[72]; +}; + +struct [[gnu::packed]] GUIDPartitionHeader +{ + u32 sig[2]; + u32 revision; + u32 header_size; + u32 crc32_header; + u32 reserved; + u64 current_lba; + u64 backup_lba; + + u64 first_usable_lba; + u64 last_usable_lba; + + u64 disk_guid1[2]; + + u64 partition_array_start_lba; + + u32 entries_count; + u32 partition_entry_size; + u32 crc32_entries_array; +}; + +Result<NonnullOwnPtr<GUIDPartitionTable>, PartitionTable::Error> GUIDPartitionTable::try_to_initialize(const StorageDevice& device) +{ + auto table = make<GUIDPartitionTable>(device); + if (!table->is_valid()) + return { PartitionTable::Error::Invalid }; + return table; +} + +GUIDPartitionTable::GUIDPartitionTable(const StorageDevice& device) + : MBRPartitionTable(device) +{ + m_cached_header = ByteBuffer::create_zeroed(m_device->block_size()); + ASSERT(partitions_count() == 0); + if (!initialize()) + m_valid = false; +} + +const GUIDPartitionHeader& GUIDPartitionTable::header() const +{ + return *(const GUIDPartitionHeader*)m_cached_header.data(); +} + +bool GUIDPartitionTable::initialize() +{ + ASSERT(m_cached_header.data() != nullptr); + + auto first_gpt_block = (m_device->block_size() == 512) ? 1 : 0; + + auto buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_header.data()); + if (!m_device->read_block(first_gpt_block, buffer)) { + return false; + } + +#ifdef GPT_DEBUG + klog() << "GUIDPartitionTable: signature - 0x" << String::format("%x", header().sig[1]) << String::format("%x", header().sig[0]); +#endif + + if (header().sig[0] != GPT_SIGNATURE && header().sig[1] != GPT_SIGNATURE2) { + klog() << "GUIDPartitionTable: bad signature 0x" << String::format("%x", header().sig[1]) << String::format("%x", header().sig[0]); + return false; + } + + auto entries_buffer = ByteBuffer::create_zeroed(m_device->block_size()); + auto raw_entries_buffer = UserOrKernelBuffer::for_kernel_buffer(entries_buffer.data()); + size_t raw_byte_index = header().partition_array_start_lba * m_device->block_size(); + for (size_t entry_index = 0; entry_index < header().entries_count; entry_index++) { + + if (!m_device->read_block((raw_byte_index / m_device->block_size()), raw_entries_buffer)) { + return false; + } + auto* entries = (const GPTPartitionEntry*)entries_buffer.data(); + auto& entry = entries[entry_index % (m_device->block_size() / (size_t)header().partition_entry_size)]; + ByteBuffer partition_type = ByteBuffer::copy(entry.partition_guid, 16); + + if (is_unused_entry(partition_type)) { + raw_byte_index += header().partition_entry_size; + continue; + } + + ByteBuffer unique_guid = ByteBuffer::copy(entry.unique_guid, 16); + String name = entry.partition_name; + dbg() << "Detected GPT partition (entry " << entry_index << ") , offset " << entry.first_lba << " , limit " << entry.last_lba; + m_partitions.append(DiskPartitionMetadata({ entry.first_lba, entry.last_lba, partition_type })); + raw_byte_index += header().partition_entry_size; + } + + return true; +} + +bool GUIDPartitionTable::is_unused_entry(ByteBuffer partition_type) const +{ + ASSERT(partition_type.size() == 16); + for (size_t byte_index = 0; byte_index < 16; byte_index++) { + if (partition_type[byte_index] != 0) + return false; + } + return true; +} + +} diff --git a/Kernel/Storage/Partition/GPTPartitionTable.h b/Kernel/Storage/Partition/GUIDPartitionTable.h index b73d0ff005..ecdfd43bc9 100644 --- a/Kernel/Storage/Partition/GPTPartitionTable.h +++ b/Kernel/Storage/Partition/GUIDPartitionTable.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,65 +27,31 @@ #pragma once #include <AK/RefPtr.h> +#include <AK/Result.h> #include <AK/Types.h> #include <AK/Vector.h> -#include <Kernel/Storage/Partition/DiskPartition.h> +#include <Kernel/Storage/Partition/MBRPartitionTable.h> namespace Kernel { -#define GPT_SIGNATURE2 0x54524150 -#define GPT_SIGNATURE 0x20494645 -#define BytesPerSector 512 - -struct [[gnu::packed]] GPTPartitionEntry -{ - u32 partition_guid[4]; - u32 unique_guid[4]; - - u32 first_lba[2]; - u32 last_lba[2]; - - u64 attributes; - u8 partition_name[72]; -}; - -struct [[gnu::packed]] GPTPartitionHeader -{ - u32 sig[2]; - u32 revision; - u32 header_size; - u32 crc32_header; - u32 reserved; - u64 current_lba; - u64 backup_lba; - - u64 first_usable_lba; - u64 last_usable_lba; - - u64 disk_guid1[2]; - - u64 partition_array_start_lba; - - u32 entries_count; - u32 partition_entry_size; - u32 crc32_entries_array; -}; - -class GPTPartitionTable { - +struct GUIDPartitionHeader; +class GUIDPartitionTable final : public MBRPartitionTable { public: - explicit GPTPartitionTable(BlockDevice&); - ~GPTPartitionTable(); + virtual ~GUIDPartitionTable() {}; - bool initialize(); - RefPtr<DiskPartition> partition(unsigned index); + static Result<NonnullOwnPtr<GUIDPartitionTable>, PartitionTable::Error> try_to_initialize(const StorageDevice&); + explicit GUIDPartitionTable(const StorageDevice&); -private: - NonnullRefPtr<BlockDevice> m_device; + virtual Type type() const override { return Type::GPT; }; + virtual bool is_valid() const override { return m_valid; }; - const GPTPartitionHeader& header() const; +private: + bool is_unused_entry(ByteBuffer) const; + const GUIDPartitionHeader& header() const; + bool initialize(); - u8 m_cached_header[512]; + bool m_valid { true }; + ByteBuffer m_cached_header; }; } diff --git a/Kernel/Storage/Partition/MBRPartitionTable.cpp b/Kernel/Storage/Partition/MBRPartitionTable.cpp index e2538ad87c..931081c628 100644 --- a/Kernel/Storage/Partition/MBRPartitionTable.cpp +++ b/Kernel/Storage/Partition/MBRPartitionTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,38 +33,105 @@ namespace Kernel { -MBRPartitionTable::MBRPartitionTable(NonnullRefPtr<BlockDevice> device) - : m_device(move(device)) +#define MBR_SIGNATURE 0xaa55 +#define MBR_PROTECTIVE 0xEE +#define EBR_CHS_CONTAINER 0x05 +#define EBR_LBA_CONTAINER 0x0F + +Result<NonnullOwnPtr<MBRPartitionTable>, PartitionTable::Error> MBRPartitionTable::try_to_initialize(const StorageDevice& device) { + auto table = make<MBRPartitionTable>(device); + if (table->contains_ebr()) + return { PartitionTable::Error::ConatinsEBR }; + if (table->is_protective_mbr()) + return { PartitionTable::Error::MBRProtective }; + if (!table->is_valid()) + return { PartitionTable::Error::Invalid }; + return table; } -MBRPartitionTable::~MBRPartitionTable() +OwnPtr<MBRPartitionTable> MBRPartitionTable::try_to_initialize(const StorageDevice& device, u32 start_lba) { + auto table = make<MBRPartitionTable>(device, start_lba); + if (!table->is_valid()) + return nullptr; + return table; } -const MBRPartitionHeader& MBRPartitionTable::header() const +bool MBRPartitionTable::read_boot_record() { - return *reinterpret_cast<const MBRPartitionHeader*>(m_cached_header); + auto buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_header.data()); + if (!m_device->read_block(m_start_lba, buffer)) + return false; + m_header_valid = true; + return m_header_valid; } -bool MBRPartitionTable::initialize() +MBRPartitionTable::MBRPartitionTable(const StorageDevice& device, u32 start_lba) + : PartitionTable(device) + , m_start_lba(start_lba) + , m_cached_header(ByteBuffer::create_zeroed(m_device->block_size())) { - auto header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_header); - if (!m_device->read_block(0, header_buffer)) { - return false; + if (!read_boot_record() || !initialize()) + return; + + m_header_valid = true; + + auto& header = this->header(); + for (size_t index = 0; index < 4; index++) { + auto& entry = header.entry[index]; + if (entry.offset == 0x00) { + continue; + } + auto partition_type = ByteBuffer::create_zeroed(sizeof(u8)); + partition_type.data()[0] = entry.type; + m_partitions.append(DiskPartitionMetadata({ entry.offset, (entry.offset + entry.length), partition_type })); } + m_valid = true; +} + +MBRPartitionTable::MBRPartitionTable(const StorageDevice& device) + : PartitionTable(device) + , m_start_lba(0) + , m_cached_header(ByteBuffer::create_zeroed(m_device->block_size())) +{ + if (!read_boot_record() || contains_ebr() || is_protective_mbr() || !initialize()) + return; auto& header = this->header(); + for (size_t index = 0; index < 4; index++) { + auto& entry = header.entry[index]; + if (entry.offset == 0x00) { + continue; + } + auto partition_type = ByteBuffer::create_zeroed(sizeof(u8)); + partition_type.data()[0] = entry.type; + m_partitions.append(DiskPartitionMetadata({ entry.offset, (entry.offset + entry.length), partition_type })); + } + m_valid = true; +} + +MBRPartitionTable::~MBRPartitionTable() +{ +} + +const MBRPartitionTable::Header& MBRPartitionTable::header() const +{ + return *(const MBRPartitionTable::Header*)m_cached_header.data(); +} +bool MBRPartitionTable::initialize() +{ + auto& header = this->header(); #ifdef MBR_DEBUG - klog() << "MBRPartitionTable::initialize: mbr_signature=0x" << String::format("%x", header.mbr_signature); -#endif + klog() << "Master Boot Record: mbr_signature=0x" << String::format("%x", header.mbr_signature); + +#endif if (header.mbr_signature != MBR_SIGNATURE) { - klog() << "MBRPartitionTable::initialize: bad mbr signature 0x" << String::format("%x", header.mbr_signature); + klog() << "Master Boot Record: invalid signature"; return false; } - return true; } @@ -82,35 +149,4 @@ bool MBRPartitionTable::is_protective_mbr() const return header().entry[0].type == MBR_PROTECTIVE; } -RefPtr<DiskPartition> MBRPartitionTable::partition(unsigned index) -{ - ASSERT(index >= 1 && index <= 4); - - auto& header = this->header(); - auto& entry = header.entry[index - 1]; - - if (header.mbr_signature != MBR_SIGNATURE) { - klog() << "MBRPartitionTable::initialize: bad mbr signature - not initialized? 0x" << String::format("%x", header.mbr_signature); - return nullptr; - } - -#ifdef MBR_DEBUG - klog() << "MBRPartitionTable::partition: status=0x" << String::format("%x", entry.status) << " offset=0x" << String::format("%x", entry.offset); -#endif - - if (entry.offset == 0x00) { -#ifdef MBR_DEBUG - klog() << "MBRPartitionTable::partition: missing partition requested index=" << index; -#endif - - return nullptr; - } - -#ifdef MBR_DEBUG - klog() << "MBRPartitionTable::partition: found partition index=" << index << " type=" << String::format("%x", entry.type); -#endif - - return DiskPartition::create(m_device, entry.offset, (entry.offset + entry.length)); -} - } diff --git a/Kernel/Storage/Partition/MBRPartitionTable.h b/Kernel/Storage/Partition/MBRPartitionTable.h index 74b7d06b83..93f134ad43 100644 --- a/Kernel/Storage/Partition/MBRPartitionTable.h +++ b/Kernel/Storage/Partition/MBRPartitionTable.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,57 +26,64 @@ #pragma once +#include <AK/ByteBuffer.h> #include <AK/RefPtr.h> +#include <AK/Result.h> #include <AK/Vector.h> -#include <Kernel/Storage/Partition/DiskPartition.h> +#include <Kernel/Storage/Partition/PartitionTable.h> namespace Kernel { -#define MBR_SIGNATURE 0xaa55 -#define MBR_PROTECTIVE 0xEE -#define EBR_CHS_CONTAINER 0x05 -#define EBR_LBA_CONTAINER 0x0F - -struct [[gnu::packed]] MBRPartitionEntry -{ - u8 status; - u8 chs1[3]; - u8 type; - u8 chs2[3]; - u32 offset; - u32 length; -}; - -struct [[gnu::packed]] MBRPartitionHeader -{ - u8 code1[218]; - u16 ts_zero; - u8 ts_drive, ts_seconds, ts_minutes, ts_hours; - u8 code2[216]; - u32 disk_signature; - u16 disk_signature_zero; - MBRPartitionEntry entry[4]; - u16 mbr_signature; -}; - -class MBRPartitionTable { - AK_MAKE_ETERNAL +class MBRPartitionTable : public PartitionTable { +public: + struct [[gnu::packed]] Entry + { + u8 status; + u8 chs1[3]; + u8 type; + u8 chs2[3]; + u32 offset; + u32 length; + }; + struct [[gnu::packed]] Header + { + u8 code1[218]; + u16 ts_zero; + u8 ts_drive; + u8 ts_seconds; + u8 ts_minutes; + u8 ts_hours; + u8 code2[216]; + u32 disk_signature; + u16 disk_signature_zero; + Entry entry[4]; + u16 mbr_signature; + }; public: - explicit MBRPartitionTable(NonnullRefPtr<BlockDevice>); ~MBRPartitionTable(); - bool initialize(); + static Result<NonnullOwnPtr<MBRPartitionTable>, PartitionTable::Error> try_to_initialize(const StorageDevice&); + static OwnPtr<MBRPartitionTable> try_to_initialize(const StorageDevice&, u32 start_lba); + explicit MBRPartitionTable(const StorageDevice&); + MBRPartitionTable(const StorageDevice&, u32 start_lba); + bool is_protective_mbr() const; bool contains_ebr() const; - RefPtr<DiskPartition> partition(unsigned index); - -private: - NonnullRefPtr<BlockDevice> m_device; + virtual Type type() const override { return Type::MBR; }; + virtual bool is_valid() const override { return m_valid; }; - const MBRPartitionHeader& header() const; +protected: + const Header& header() const; + bool is_header_valid() const { return m_header_valid; }; - u8 m_cached_header[512]; +private: + bool read_boot_record(); + bool initialize(); + bool m_valid { false }; + bool m_header_valid { false }; + const u32 m_start_lba; + ByteBuffer m_cached_header; + size_t m_partitions_count { 0 }; }; - } diff --git a/Kernel/Storage/Partition/PartitionTable.cpp b/Kernel/Storage/Partition/PartitionTable.cpp new file mode 100644 index 0000000000..4f719f872a --- /dev/null +++ b/Kernel/Storage/Partition/PartitionTable.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <Kernel/Storage/Partition/PartitionTable.h> + +namespace Kernel { +PartitionTable::PartitionTable(const StorageDevice& device) + : m_device(device) +{ +} + +Optional<DiskPartitionMetadata> PartitionTable::partition(unsigned index) +{ + if (index > partitions_count()) + return {}; + return m_partitions[index]; +} + +} diff --git a/Kernel/Storage/Partition/PartitionTable.h b/Kernel/Storage/Partition/PartitionTable.h new file mode 100644 index 0000000000..324284492e --- /dev/null +++ b/Kernel/Storage/Partition/PartitionTable.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/RefPtr.h> +#include <AK/Vector.h> +#include <Kernel/Storage/Partition/DiskPartition.h> +#include <Kernel/Storage/Partition/DiskPartitionMetadata.h> +#include <Kernel/Storage/StorageDevice.h> + +namespace Kernel { + +class PartitionTable { +public: + enum class Type { + MBR, + EBR, + GPT, + BSD + }; + enum class Error { + Invalid, + MBRProtective, + ConatinsEBR, + }; + +public: + Optional<DiskPartitionMetadata> partition(unsigned index); + size_t partitions_count() const { return m_partitions.size(); } + virtual Type type() const = 0; + virtual ~PartitionTable() { } + virtual bool is_valid() const = 0; + + Vector<DiskPartitionMetadata> partitions() const { return m_partitions; } + +protected: + explicit PartitionTable(const StorageDevice&); + + NonnullRefPtr<StorageDevice> m_device; + Vector<DiskPartitionMetadata> m_partitions; +}; + +} diff --git a/Kernel/Storage/StorageDevice.h b/Kernel/Storage/StorageDevice.h index 21840b4d92..1fe30c0bd2 100644 --- a/Kernel/Storage/StorageDevice.h +++ b/Kernel/Storage/StorageDevice.h @@ -29,11 +29,13 @@ #include <Kernel/Devices/BlockDevice.h> #include <Kernel/Interrupts/IRQHandler.h> #include <Kernel/Lock.h> +#include <Kernel/Storage/Partition/DiskPartition.h> #include <Kernel/Storage/StorageController.h> namespace Kernel { class StorageDevice : public BlockDevice { + friend class StorageManagement; AK_MAKE_ETERNAL public: enum class Type : u8 { @@ -63,6 +65,7 @@ protected: private: NonnullRefPtr<StorageController> m_storage_controller; + NonnullRefPtrVector<DiskPartition> m_partitions; size_t m_max_addressable_block; }; diff --git a/Kernel/Storage/StorageManagement.cpp b/Kernel/Storage/StorageManagement.cpp index 78ae8974d0..4f3acf4fe0 100644 --- a/Kernel/Storage/StorageManagement.cpp +++ b/Kernel/Storage/StorageManagement.cpp @@ -25,8 +25,12 @@ */ #include <Kernel/Devices/BlockDevice.h> +#include <Kernel/FileSystem/Ext2FileSystem.h> #include <Kernel/PCI/Access.h> #include <Kernel/Storage/IDEController.h> +#include <Kernel/Storage/Partition/EBRPartitionTable.h> +#include <Kernel/Storage/Partition/GUIDPartitionTable.h> +#include <Kernel/Storage/Partition/MBRPartitionTable.h> #include <Kernel/Storage/StorageManagement.h> namespace Kernel { @@ -35,10 +39,82 @@ static StorageManagement* s_the; StorageManagement::StorageManagement(String root_device, bool force_pio) : m_controllers(enumerate_controllers(force_pio)) + , m_storage_devices(enumerate_storage_devices()) + , m_disk_partitions(enumerate_disk_partitions()) , m_boot_device(determine_boot_device(root_device)) + , m_boot_block_device(determine_boot_block_device(root_device)) { } +NonnullRefPtrVector<StorageController> StorageManagement::enumerate_controllers(bool force_pio) const +{ + NonnullRefPtrVector<StorageController> controllers; + PCI::enumerate([&](const PCI::Address& address, PCI::ID) { + if (PCI::get_class(address) == 0x1 && PCI::get_subclass(address) == 0x1) { + controllers.append(IDEController::initialize(address, force_pio)); + } + }); + return controllers; +} + +NonnullRefPtrVector<StorageDevice> StorageManagement::enumerate_storage_devices() const +{ + ASSERT(!m_controllers.is_empty()); + NonnullRefPtrVector<StorageDevice> devices; + for (auto& controller : m_controllers) { + for (size_t device_index = 0; device_index < controller.devices_count(); device_index++) { + auto device = controller.device(device_index); + if (device.is_null()) + continue; + devices.append(device.release_nonnull()); + } + } + return devices; +} + +OwnPtr<PartitionTable> StorageManagement::try_to_initialize_partition_table(const StorageDevice& device) const +{ + auto mbr_table_or_result = MBRPartitionTable::try_to_initialize(device); + if (!mbr_table_or_result.is_error()) + return move(mbr_table_or_result.value()); + if (mbr_table_or_result.error() == PartitionTable::Error::MBRProtective) { + auto gpt_table_or_result = GUIDPartitionTable::try_to_initialize(device); + if (gpt_table_or_result.is_error()) + return nullptr; + return move(gpt_table_or_result.value()); + } + if (mbr_table_or_result.error() == PartitionTable::Error::ConatinsEBR) { + auto ebr_table_or_result = EBRPartitionTable::try_to_initialize(device); + if (ebr_table_or_result.is_error()) + return nullptr; + return move(ebr_table_or_result.value()); + } + return nullptr; +} + +NonnullRefPtrVector<DiskPartition> StorageManagement::enumerate_disk_partitions() const +{ + ASSERT(!m_storage_devices.is_empty()); + NonnullRefPtrVector<DiskPartition> partitions; + size_t device_index = 0; + for (auto& device : m_storage_devices) { + auto partition_table = try_to_initialize_partition_table(device); + if (!partition_table) + continue; + for (size_t partition_index = 0; partition_index < partition_table->partitions_count(); partition_index++) { + auto partition_metadata = partition_table->partition(partition_index); + if (!partition_metadata.has_value()) + continue; + // FIXME: Try to not hardcode a maximum of 16 partitions per drive! + auto disk_partition = DiskPartition::create(const_cast<StorageDevice&>(device), (partition_index + (16 * device_index)), partition_metadata.value()); + partitions.append(disk_partition); + const_cast<StorageDevice&>(device).m_partitions.append(disk_partition); + } + device_index++; + } + return partitions; +} + NonnullRefPtr<StorageDevice> StorageManagement::determine_boot_device(String root_device) const { ASSERT(!m_controllers.is_empty()); @@ -54,37 +130,48 @@ NonnullRefPtr<StorageDevice> StorageManagement::determine_boot_device(String roo } size_t drive_index = (u8)drive_letter - (u8)'a'; - auto devices = storage_devices(); - if (drive_index >= devices.size()) { + if (drive_index >= m_storage_devices.size()) { klog() << "init_stage2: invalid selection of hard drive."; Processor::halt(); } - return devices[drive_index]; + return m_storage_devices[drive_index]; } -NonnullRefPtrVector<StorageController> StorageManagement::enumerate_controllers(bool force_pio) const +NonnullRefPtr<BlockDevice> StorageManagement::determine_boot_block_device(String root_device) const { - NonnullRefPtrVector<StorageController> controllers; - PCI::enumerate([&](const PCI::Address& address, PCI::ID) { - if (PCI::get_class(address) == 0x1 && PCI::get_subclass(address) == 0x1) { - controllers.append(IDEController::initialize(address, force_pio)); - } - }); - return controllers; + auto determined_boot_device = m_boot_device; + root_device = root_device.substring(strlen("/dev/hda"), root_device.length() - strlen("/dev/hda")); + if (!root_device.length()) + return determined_boot_device; + + auto partition_number = root_device.to_uint(); + + if (!partition_number.has_value()) { + klog() << "init_stage2: couldn't parse partition number from root kernel parameter"; + Processor::halt(); + } + + if (partition_number.value() > m_boot_device->m_partitions.size()) { + klog() << "init_stage2: invalid partition number!"; + Processor::halt(); + } + + return m_boot_device->m_partitions[partition_number.value() - 1]; } -NonnullRefPtrVector<StorageDevice> StorageManagement::storage_devices() const +NonnullRefPtr<BlockDevice> StorageManagement::boot_block_device() const { - NonnullRefPtrVector<StorageDevice> devices; - for (auto& controller : m_controllers) { - for (size_t index = 0; index < controller.devices_count(); index++) { - auto device = controller.device(index); - if (device.is_null()) - continue; - devices.append(device.release_nonnull()); - } + return m_boot_block_device; +} + +NonnullRefPtr<FS> StorageManagement::root_filesystem() const +{ + auto e2fs = Ext2FS::create(*FileDescription::create(boot_block_device())); + if (!e2fs->initialize()) { + klog() << "init_stage2: couldn't open root filesystem"; + Processor::halt(); } - return devices; + return e2fs; } bool StorageManagement::initialized() @@ -103,10 +190,6 @@ StorageManagement& StorageManagement::the() return *s_the; } -NonnullRefPtr<StorageDevice> StorageManagement::boot_device() const -{ - return m_boot_device; -} NonnullRefPtrVector<StorageController> StorageManagement::ide_controllers() const { NonnullRefPtrVector<StorageController> ide_controllers; @@ -116,5 +199,4 @@ NonnullRefPtrVector<StorageController> StorageManagement::ide_controllers() cons } return ide_controllers; } - } diff --git a/Kernel/Storage/StorageManagement.h b/Kernel/Storage/StorageManagement.h index c20b2fc698..9cf624d2b6 100644 --- a/Kernel/Storage/StorageManagement.h +++ b/Kernel/Storage/StorageManagement.h @@ -29,11 +29,14 @@ #include <AK/NonnullRefPtr.h> #include <AK/NonnullRefPtrVector.h> #include <AK/Types.h> +#include <Kernel/FileSystem/FileSystem.h> +#include <Kernel/Storage/Partition/DiskPartition.h> #include <Kernel/Storage/StorageController.h> #include <Kernel/Storage/StorageDevice.h> namespace Kernel { +class PartitionTable; class StorageManagement { AK_MAKE_ETERNAL; @@ -43,16 +46,27 @@ public: static void initialize(String root_device, bool force_pio); static StorageManagement& the(); - NonnullRefPtr<StorageDevice> boot_device() const; + NonnullRefPtr<FS> root_filesystem() const; + NonnullRefPtrVector<StorageController> ide_controllers() const; - NonnullRefPtrVector<StorageDevice> storage_devices() const; private: + NonnullRefPtr<BlockDevice> boot_block_device() const; + NonnullRefPtrVector<StorageController> enumerate_controllers(bool force_pio) const; + NonnullRefPtrVector<StorageDevice> enumerate_storage_devices() const; + NonnullRefPtrVector<DiskPartition> enumerate_disk_partitions() const; + NonnullRefPtr<StorageDevice> determine_boot_device(String root_device) const; + NonnullRefPtr<BlockDevice> determine_boot_block_device(String root_device) const; + + OwnPtr<PartitionTable> try_to_initialize_partition_table(const StorageDevice&) const; NonnullRefPtrVector<StorageController> m_controllers; + NonnullRefPtrVector<StorageDevice> m_storage_devices; + NonnullRefPtrVector<DiskPartition> m_disk_partitions; NonnullRefPtr<StorageDevice> m_boot_device; + NonnullRefPtr<BlockDevice> m_boot_block_device; }; } diff --git a/Kernel/init.cpp b/Kernel/init.cpp index e124e1ddb2..f261bbd575 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -61,10 +61,6 @@ #include <Kernel/RTC.h> #include <Kernel/Random.h> #include <Kernel/Scheduler.h> -#include <Kernel/Storage/Partition/DiskPartition.h> -#include <Kernel/Storage/Partition/EBRPartitionTable.h> -#include <Kernel/Storage/Partition/GPTPartitionTable.h> -#include <Kernel/Storage/Partition/MBRPartitionTable.h> #include <Kernel/Storage/StorageManagement.h> #include <Kernel/TTY/PTYMultiplexer.h> #include <Kernel/TTY/VirtualConsole.h> @@ -271,73 +267,7 @@ void init_stage2(void*) auto root = kernel_command_line().lookup("root").value_or("/dev/hda"); StorageManagement::initialize(root, force_pio); - NonnullRefPtr<BlockDevice> root_dev = StorageManagement::the().boot_device(); - - root = root.substring(strlen("/dev/hda"), root.length() - strlen("/dev/hda")); - - if (root.length()) { - auto partition_number = root.to_uint(); - - if (!partition_number.has_value()) { - klog() << "init_stage2: couldn't parse partition number from root kernel parameter"; - Processor::halt(); - } - - MBRPartitionTable mbr(root_dev); - - if (!mbr.initialize()) { - klog() << "init_stage2: couldn't read MBR from disk"; - Processor::halt(); - } - - if (mbr.is_protective_mbr()) { - dbg() << "GPT Partitioned Storage Detected!"; - GPTPartitionTable gpt(root_dev); - if (!gpt.initialize()) { - klog() << "init_stage2: couldn't read GPT from disk"; - Processor::halt(); - } - auto partition = gpt.partition(partition_number.value()); - if (!partition) { - klog() << "init_stage2: couldn't get partition " << partition_number.value(); - Processor::halt(); - } - root_dev = *partition; - } else { - dbg() << "MBR Partitioned Storage Detected!"; - if (mbr.contains_ebr()) { - EBRPartitionTable ebr(root_dev); - if (!ebr.initialize()) { - klog() << "init_stage2: couldn't read EBR from disk"; - Processor::halt(); - } - auto partition = ebr.partition(partition_number.value()); - if (!partition) { - klog() << "init_stage2: couldn't get partition " << partition_number.value(); - Processor::halt(); - } - root_dev = *partition; - } else { - if (partition_number.value() < 1 || partition_number.value() > 4) { - klog() << "init_stage2: invalid partition number " << partition_number.value() << "; expected 1 to 4"; - Processor::halt(); - } - auto partition = mbr.partition(partition_number.value()); - if (!partition) { - klog() << "init_stage2: couldn't get partition " << partition_number.value(); - Processor::halt(); - } - root_dev = *partition; - } - } - } - auto e2fs = Ext2FS::create(*FileDescription::create(root_dev)); - if (!e2fs->initialize()) { - klog() << "init_stage2: couldn't open root filesystem"; - Processor::halt(); - } - - if (!VFS::the().mount_root(e2fs)) { + if (!VFS::the().mount_root(StorageManagement::the().root_filesystem())) { klog() << "VFS::mount_root failed"; Processor::halt(); } diff --git a/Meta/grub-ebr.cfg b/Meta/grub-ebr.cfg index f865bb9162..ef83b24ba5 100644 --- a/Meta/grub-ebr.cfg +++ b/Meta/grub-ebr.cfg @@ -2,21 +2,21 @@ timeout=1 menuentry 'SerenityOS (normal)' { root=hd0,5 - multiboot /boot/Kernel root=/dev/hda5 + multiboot /boot/Kernel root=/dev/hda4 } menuentry 'SerenityOS (text mode)' { root=hd0,5 - multiboot /boot/Kernel boot_mode=text root=/dev/hda5 + multiboot /boot/Kernel boot_mode=text root=/dev/hda4 } menuentry 'SerenityOS (No ACPI)' { root=hd0,5 - multiboot /boot/Kernel root=/dev/hda5 acpi=off + multiboot /boot/Kernel root=/dev/hda4 acpi=off } menuentry 'SerenityOS (with serial debug)' { root=hd0,5 - multiboot /boot/Kernel serial_debug root=/dev/hda5 + multiboot /boot/Kernel serial_debug root=/dev/hda4 } |