diff options
Diffstat (limited to 'Kernel')
19 files changed, 620 insertions, 542 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(); } |