diff --git a/Kernel/Devices/EBRPartitionTable.cpp b/Kernel/Devices/EBRPartitionTable.cpp new file mode 100644 index 0000000000..a5fbb04462 --- /dev/null +++ b/Kernel/Devices/EBRPartitionTable.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2020, Liav A. + * 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 +#include + +#define EBR_DEBUG + +EBRPartitionTable::EBRPartitionTable(NonnullRefPtr device) + : m_device(move(device)) +{ +} + +EBRPartitionTable::~EBRPartitionTable() +{ +} + +const MBRPartitionHeader& EBRPartitionTable::header() const +{ + return *reinterpret_cast(m_cached_mbr_header); +} + +const EBRPartitionExtension& EBRPartitionTable::ebr_extension() const +{ + return *reinterpret_cast(m_cached_ebr_header); +} + +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() +{ + if (!m_device->read_block(0, m_cached_mbr_header)) { + return false; + } + auto& header = this->header(); + + m_ebr_container_id = index_of_ebr_container() + 1; + +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::initialize: MBR_signature=%#x\n", header.mbr_signature); +#endif + + if (header.mbr_signature != MBR_SIGNATURE) { + kprintf("EBRPartitionTable::initialize: bad MBR signature %#x\n", header.mbr_signature); + return false; + } + + auto& ebr_entry = header.entry[m_ebr_container_id - 1]; + if (!m_device->read_block(ebr_entry.offset, m_cached_ebr_header)) { + 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, m_cached_ebr_header)) { + return false; + } + } + + m_ebr_chained_extensions_count = index; + + kprintf("EBRPartitionTable::initialize: Extended partitions count - %d\n", m_ebr_chained_extensions_count); + + return true; +} + +RefPtr EBRPartitionTable::get_non_extended_partition(unsigned index) +{ + auto& header = this->header(); + auto& entry = header.entry[index - 1]; + +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: status=%#x offset=%#x\n", entry.status, entry.offset); +#endif + + if (entry.offset == 0x00) { +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: missing partition requested index=%d\n", index); +#endif + + return nullptr; + } + +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: found partition index=%d type=%x\n", index, entry.type); +#endif + + return DiskPartition::create(m_device, entry.offset, (entry.offset + entry.length)); +} + +RefPtr EBRPartitionTable::get_extended_partition(unsigned index) +{ + + unsigned relative_index = index - m_ebr_container_id; + auto& header = this->header(); + +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: relative index %d\n", relative_index); +#endif + + auto& ebr_entry = header.entry[m_ebr_container_id - 1]; +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: Extended partition, offset 0x%x, type %x\n", ebr_entry.offset, ebr_entry.type); +#endif + + if (!m_device->read_block(ebr_entry.offset, m_cached_ebr_header)) { + return nullptr; + } + size_t i = 0; + while (i < relative_index) { +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: logical partition, relative offset 0x%x, type %x\n", ebr_extension().entry.offset, ebr_extension().entry.type); + kprintf("EBRPartitionTable::partition: next logical partition, relative offset 0x%x, type %x\n", ebr_extension().next_chained_ebr_extension.offset, 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, m_cached_ebr_header)) { + return nullptr; + } + } + +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: status=%#x offset=%#x\n", ebr_extension().entry.status, ebr_extension().entry.offset + ebr_entry.offset); +#endif + + if (ebr_extension().entry.offset == 0x00) { +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: missing partition requested index=%d\n", index); +#endif + + return nullptr; + } + +#ifdef EBR_DEBUG + kprintf("EBRPartitionTable::partition: found partition index=%d type=%x\n", index, 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 EBRPartitionTable::partition(unsigned index) +{ + ASSERT(index >= 1 && index <= m_ebr_chained_extensions_count + 4); + + auto& header = this->header(); + if (header.mbr_signature != MBR_SIGNATURE) { + kprintf("EBRPartitionTable::initialize: bad MBR signature - not initalized? %#x\n", 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/Devices/EBRPartitionTable.h b/Kernel/Devices/EBRPartitionTable.h new file mode 100644 index 0000000000..9245fc7626 --- /dev/null +++ b/Kernel/Devices/EBRPartitionTable.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020, Liav A. + * 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 +#include +#include +#include +#include + +struct [[gnu::packed]] EBRPartitionExtension +{ + u8 unused_area[446]; + MBRPartitionEntry entry; + MBRPartitionEntry next_chained_ebr_extension; + MBRPartitionEntry unused[2]; + u16 mbr_signature; +}; + +class EBRPartitionTable { + +public: + explicit EBRPartitionTable(NonnullRefPtr); + ~EBRPartitionTable(); + + bool initialize(); + RefPtr partition(unsigned index); + +private: + int index_of_ebr_container() const; + NonnullRefPtr m_device; + + const MBRPartitionHeader& header() const; + const EBRPartitionExtension& ebr_extension() const; + + bool index_is_extended_partition(unsigned index) const; + + RefPtr get_extended_partition(unsigned index); + RefPtr 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]; +}; diff --git a/Kernel/Devices/MBRPartitionTable.cpp b/Kernel/Devices/MBRPartitionTable.cpp index 254376e9fd..8880533698 100644 --- a/Kernel/Devices/MBRPartitionTable.cpp +++ b/Kernel/Devices/MBRPartitionTable.cpp @@ -63,6 +63,15 @@ bool MBRPartitionTable::initialize() return true; } +bool MBRPartitionTable::contains_ebr() const +{ + for (int i = 0; i < 4; i++) { + if (header().entry[i].type == EBR_CHS_CONTAINER || header().entry[i].type == EBR_LBA_CONTAINER) + return true; + } + return false; +} + bool MBRPartitionTable::is_protective_mbr() const { return header().entry[0].type == MBR_PROTECTIVE; diff --git a/Kernel/Devices/MBRPartitionTable.h b/Kernel/Devices/MBRPartitionTable.h index 9d7f033cba..61adddf19c 100644 --- a/Kernel/Devices/MBRPartitionTable.h +++ b/Kernel/Devices/MBRPartitionTable.h @@ -33,6 +33,8 @@ #define MBR_SIGNATURE 0xaa55 #define MBR_PROTECTIVE 0xEE +#define EBR_CHS_CONTAINER 0x05 +#define EBR_LBA_CONTAINER 0x0F struct [[gnu::packed]] MBRPartitionEntry { @@ -60,11 +62,12 @@ class MBRPartitionTable { AK_MAKE_ETERNAL public: - MBRPartitionTable(NonnullRefPtr); + explicit MBRPartitionTable(NonnullRefPtr); ~MBRPartitionTable(); bool initialize(); bool is_protective_mbr() const; + bool contains_ebr() const; RefPtr partition(unsigned index); private: diff --git a/Kernel/Makefile b/Kernel/Makefile index b5aac6257b..92ccf9467e 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -25,6 +25,7 @@ OBJS = \ Devices/FloppyDiskDevice.o \ Devices/FullDevice.o \ Devices/GPTPartitionTable.o \ + Devices/EBRPartitionTable.o \ Devices/KeyboardDevice.o \ Devices/MBRPartitionTable.o \ Devices/MBVGADevice.o \ diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 7de54fa3dd..a5b2348a21 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -273,16 +274,30 @@ void init_stage2() root_dev = *partition; } else { dbgprintf("MBR Partitioned Storage Detected!\n"); - if (partition_number < 1 || partition_number > 4) { - kprintf("init_stage2: invalid partition number %d; expected 1 to 4\n", partition_number); - hang(); + if (mbr.contains_ebr()) { + EBRPartitionTable ebr(root_dev); + if (!ebr.initialize()) { + kprintf("init_stage2: couldn't read EBR from disk\n"); + hang(); + } + auto partition = ebr.partition(partition_number); + if (!partition) { + kprintf("init_stage2: couldn't get partition %d\n", partition_number); + hang(); + } + root_dev = *partition; + } else { + if (partition_number < 1 || partition_number > 4) { + kprintf("init_stage2: invalid partition number %d; expected 1 to 4\n", partition_number); + hang(); + } + auto partition = mbr.partition(partition_number); + if (!partition) { + kprintf("init_stage2: couldn't get partition %d\n", partition_number); + hang(); + } + root_dev = *partition; } - auto partition = mbr.partition(partition_number); - if (!partition) { - kprintf("init_stage2: couldn't get partition %d\n", partition_number); - hang(); - } - root_dev = *partition; } }