diff --git a/Kernel/Devices/DiskPartition.cpp b/Kernel/Devices/DiskPartition.cpp index e49521a3db..56ebe4c894 100644 --- a/Kernel/Devices/DiskPartition.cpp +++ b/Kernel/Devices/DiskPartition.cpp @@ -2,15 +2,16 @@ // #define OFFD_DEBUG -NonnullRefPtr DiskPartition::create(DiskDevice& device, unsigned block_offset) +NonnullRefPtr DiskPartition::create(DiskDevice& device, unsigned block_offset, unsigned block_limit) { - return adopt(*new DiskPartition(device, block_offset)); + return adopt(*new DiskPartition(device, block_offset, block_limit)); } -DiskPartition::DiskPartition(DiskDevice& device, unsigned block_offset) +DiskPartition::DiskPartition(DiskDevice& device, unsigned block_offset, unsigned block_limit) : DiskDevice(100, 0, device.block_size()) , m_device(device) , m_block_offset(block_offset) + , m_block_limit(block_limit) { } diff --git a/Kernel/Devices/DiskPartition.h b/Kernel/Devices/DiskPartition.h index 9b73a1f455..6ad35eb2c4 100644 --- a/Kernel/Devices/DiskPartition.h +++ b/Kernel/Devices/DiskPartition.h @@ -5,7 +5,7 @@ class DiskPartition final : public DiskDevice { public: - static NonnullRefPtr create(DiskDevice&, unsigned block_offset); + static NonnullRefPtr create(DiskDevice&, unsigned block_offset, unsigned block_limit); virtual ~DiskPartition(); virtual bool read_block(unsigned index, u8* out) const override; @@ -22,8 +22,9 @@ public: private: virtual const char* class_name() const override; - DiskPartition(DiskDevice&, unsigned block_offset); + DiskPartition(DiskDevice&, unsigned block_offset, unsigned block_limit); NonnullRefPtr m_device; unsigned m_block_offset; + unsigned m_block_limit; }; diff --git a/Kernel/Devices/GPTPartitionTable.cpp b/Kernel/Devices/GPTPartitionTable.cpp new file mode 100644 index 0000000000..a4347bb716 --- /dev/null +++ b/Kernel/Devices/GPTPartitionTable.cpp @@ -0,0 +1,75 @@ +#include +#include + +#define GPT_DEBUG + +GPTPartitionTable::GPTPartitionTable(DiskDevice& device) + : m_device(move(device)) +{ +} + +GPTPartitionTable::~GPTPartitionTable() +{ +} + +const GPTPartitionHeader& GPTPartitionTable::header() const +{ + return *reinterpret_cast(m_cached_header); +} + +bool GPTPartitionTable::initialize() +{ + if (!m_device->read_block(1, m_cached_header)) { + return false; + } + + auto& header = this->header(); + +#ifdef GPT_DEBUG + kprintf("GPTPartitionTable::initialize: gpt_signature=%#x%x\n", header.sig[1], header.sig[0]); +#endif + + if (header.sig[0] != GPT_SIGNATURE && header.sig[1] != GPT_SIGNATURE2) { + kprintf("GPTPartitionTable::initialize: bad GPT signature %#x%x\n", header.sig[1], header.sig[0]); + return false; + } + + return true; +} + +RefPtr 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) { + kprintf("GPTPartitionTable::initialize: bad gpt signature - not initalized? %#x\n", header.sig); + return nullptr; + } + + u8 entries_per_sector = BytesPerSector / header.partition_entry_size; + + GPTPartitionEntry entries[entries_per_sector]; + this->m_device->read_blocks(lba, 1, (u8*)&entries); + GPTPartitionEntry& entry = entries[((index - 1) % entries_per_sector)]; + +#ifdef GPT_DEBUG + kprintf("GPTPartitionTable::partition %d\n", index); + kprintf("GPTPartitionTable - offset = %d%d\n", entry.first_lba[1], entry.first_lba[0]); +#endif + + if (entry.first_lba[0] == 0x00) { +#ifdef GPT_DEBUG + kprintf("GPTPartitionTable::partition: missing partition requested index=%d\n", index); +#endif + + return nullptr; + } + +#ifdef GPT_DEBUG + kprintf("GPTPartitionTable::partition: found partition index=%d type=%x-%x-%x-%x\n", index, entry.partition_guid[3], entry.partition_guid[2], entry.partition_guid[1], entry.partition_guid[0]); +#endif + return DiskPartition::create(m_device, entry.first_lba[0], entry.last_lba[0]); +} diff --git a/Kernel/Devices/GPTPartitionTable.h b/Kernel/Devices/GPTPartitionTable.h new file mode 100644 index 0000000000..02d238454a --- /dev/null +++ b/Kernel/Devices/GPTPartitionTable.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include + +#define GPT_SIGNATURE2 0x54524150 +#define GPT_SIGNATURE 0x20494645 +#define BytesPerSector 512 + +struct GPTPartitionEntry { + u32 partition_guid[4]; + u32 unique_guid[4]; + + u32 first_lba[2]; + u32 last_lba[2]; + + u64 attributes; + u8 partition_name[72]; +} __attribute__((packed)); + +struct 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; +} __attribute__((packed)); + +class GPTPartitionTable { + +public: + explicit GPTPartitionTable(DiskDevice&); + ~GPTPartitionTable(); + + bool initialize(); + RefPtr partition(unsigned index); + +private: + NonnullRefPtr m_device; + + ByteBuffer read_header() const; + const GPTPartitionHeader& header() const; + + u8 m_cached_header[512]; +}; diff --git a/Kernel/Devices/MBRPartitionTable.cpp b/Kernel/Devices/MBRPartitionTable.cpp index 72ebe9e906..e031eede2a 100644 --- a/Kernel/Devices/MBRPartitionTable.cpp +++ b/Kernel/Devices/MBRPartitionTable.cpp @@ -37,6 +37,11 @@ bool MBRPartitionTable::initialize() return true; } +bool MBRPartitionTable::is_protective_mbr() const +{ + return header().entry[0].type == MBR_PROTECTIVE; +} + RefPtr MBRPartitionTable::partition(unsigned index) { ASSERT(index >= 1 && index <= 4); @@ -55,7 +60,7 @@ RefPtr MBRPartitionTable::partition(unsigned index) if (entry.offset == 0x00) { #ifdef MBR_DEBUG - kprintf("MBRPartitionTable::partition: missing partition requested index=%d\n", index); + kprintf("MBRPartitionTable::partition: missing partition requested index=%d\n", index); #endif return nullptr; @@ -65,5 +70,5 @@ RefPtr MBRPartitionTable::partition(unsigned index) kprintf("MBRPartitionTable::partition: found partition index=%d type=%x\n", index, entry.type); #endif - return DiskPartition::create(m_device, entry.offset); + return DiskPartition::create(m_device, entry.offset, (entry.offset + entry.length)); } diff --git a/Kernel/Devices/MBRPartitionTable.h b/Kernel/Devices/MBRPartitionTable.h index 35bb13db36..6316fc0b03 100644 --- a/Kernel/Devices/MBRPartitionTable.h +++ b/Kernel/Devices/MBRPartitionTable.h @@ -6,6 +6,7 @@ #include #define MBR_SIGNATURE 0xaa55 +#define MBR_PROTECTIVE 0xEE struct MBRPartitionEntry { u8 status; @@ -35,6 +36,7 @@ public: ~MBRPartitionTable(); bool initialize(); + bool is_protective_mbr() const; RefPtr partition(unsigned index); private: diff --git a/Kernel/Makefile b/Kernel/Makefile index a88112cbc3..6907836867 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -81,6 +81,7 @@ VFS_OBJS = \ Devices/DebugLogDevice.o \ Devices/DiskPartition.o \ Devices/MBRPartitionTable.o \ + Devices/GPTPartitionTable.o \ FileSystem/InodeWatcher.o \ FileSystem/FileSystem.o \ FileSystem/DiskBackedFileSystem.o \ diff --git a/Kernel/build-gpt-image-grub.sh b/Kernel/build-gpt-image-grub.sh new file mode 100755 index 0000000000..a8a42d82de --- /dev/null +++ b/Kernel/build-gpt-image-grub.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +set -e + +die() { + echo "die: $@" + exit 1 +} + +if [ $(id -u) != 0 ]; then + die "this script needs to run as root" +fi + +grub=$(which grub-install 2>/dev/null) || true +if [[ -z "$grub" ]]; then + grub=$(which grub2-install 2>/dev/null) || true +fi +if [ -z "$grub" ]; then + echo "can't find a grub-install or grub2-install binary, oh no" + exit 1 +fi +echo "using grub-install at ${grub}" + +echo "setting up disk image..." +dd if=/dev/zero of=_disk_image bs=1M count=${DISK_SIZE:-701} status=none || die "couldn't create disk image" +chown 1000:1000 _disk_image || die "couldn't adjust permissions on disk image" +echo "done" + +echo -n "creating loopback device... " +dev=$(losetup --find --partscan --show _disk_image) +if [ -z $dev ]; then + die "couldn't mount loopback device" +fi +echo "loopback device is at ${dev}" + +cleanup() { + if [ -d mnt ]; then + echo -n "unmounting filesystem... " + umount mnt || ( sleep 1 && sync && umount mnt ) + rm -rf mnt + echo "done" + fi + + if [ -e ${dev} ]; then + echo -n "cleaning up loopback device... " + losetup -d ${dev} + echo "done" + fi +} +trap cleanup EXIT + +echo -n "creating partition table... " +parted -s ${dev} mklabel gpt mkpart BIOSBOOT ext3 1MiB 8MiB mkpart OS ext2 8MiB 700MiB set 1 bios_grub || die "couldn't partition disk" +echo "done" + +echo -n "destroying old filesystem... " +dd if=/dev/zero of=${dev}p2 bs=1M count=1 status=none || die "couldn't destroy old filesystem" +echo "done" + +echo -n "creating new filesystem... " +mke2fs -q ${dev}p2 || die "couldn't create filesystem" +echo "done" + +echo -n "mounting filesystem... " +mkdir -p mnt +mount ${dev}p2 mnt/ || die "couldn't mount filesystem" +echo "done" + +./build-root-filesystem.sh + +echo -n "creating /boot... " +mkdir -p mnt/boot +echo "done" + +echo "installing grub using $grub..." +$grub --boot-directory=mnt/boot --target=i386-pc --modules="ext2 part_msdos part_gpt" ${dev} + +if [ -d mnt/boot/grub2 ]; then + cp grub_gpt.cfg mnt/boot/grub2/grub.cfg +else + cp grub_gpt.cfg mnt/boot/grub/grub.cfg +fi +echo "done" + +echo -n "installing kernel in /boot... " +cp kernel mnt/boot +echo "done" diff --git a/Kernel/grub_gpt.cfg b/Kernel/grub_gpt.cfg new file mode 100644 index 0000000000..c222d06af4 --- /dev/null +++ b/Kernel/grub_gpt.cfg @@ -0,0 +1,11 @@ +timeout=1 + +menuentry 'Serenity (normal)' { + root=hd0,2 + multiboot /boot/kernel root=/dev/hda2 +} + +menuentry 'Serenity (with serial debug)' { + root=hd0,2 + multiboot /boot/kernel serial_debug root=/dev/hda2 +} diff --git a/Kernel/init.cpp b/Kernel/init.cpp index d73b1bca1d..279c1d254e 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -96,18 +97,34 @@ VFS* vfs; } MBRPartitionTable mbr(root_dev); + if (!mbr.initialize()) { kprintf("init_stage2: couldn't read MBR from disk\n"); hang(); } - auto partition = mbr.partition(partition_number); - if (!partition) { - kprintf("init_stage2: couldn't get partition %d\n", partition_number); - hang(); + if (mbr.is_protective_mbr()) { + dbgprintf("GPT Partitioned Storage Detected!\n"); + GPTPartitionTable gpt(root_dev); + if (!gpt.initialize()) { + kprintf("init_stage2: couldn't read GPT from disk\n"); + hang(); + } + auto partition = gpt.partition(partition_number); + if (!partition) { + kprintf("init_stage2: couldn't get partition %d\n", partition_number); + hang(); + } + root_dev = *partition; + } else { + dbgprintf("MBR Partitioned Storage Detected!\n"); + auto partition = mbr.partition(partition_number); + if (!partition) { + kprintf("init_stage2: couldn't get partition %d\n", partition_number); + hang(); + } + root_dev = *partition; } - - root_dev = *partition; } auto e2fs = Ext2FS::create(root_dev);