Add GPT partition handling as virtual block devices
This commit is contained in:
114
src/kernel/fs/gpt.cpp
Normal file
114
src/kernel/fs/gpt.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/guid.h"
|
||||
#include "kutil/memory.h"
|
||||
#include "device_manager.h"
|
||||
#include "fs/gpt.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace fs {
|
||||
|
||||
const kutil::guid efi_system_part = kutil::make_guid(0xC12A7328, 0xF81F, 0x11D2, 0xBA4B, 0x00A0C93EC93B);
|
||||
const kutil::guid efi_unused_part = kutil::make_guid(0, 0, 0, 0, 0);
|
||||
|
||||
const uint64_t gpt_signature = 0x5452415020494645; // "EFI PART"
|
||||
const size_t block_size = 512;
|
||||
|
||||
struct gpt_header
|
||||
{
|
||||
uint64_t signature;
|
||||
uint32_t revision;
|
||||
uint32_t headersize;
|
||||
uint32_t crc32;
|
||||
uint32_t reserved;
|
||||
uint64_t my_lba;
|
||||
uint64_t alt_lba;
|
||||
uint64_t first_usable_lba;
|
||||
uint64_t last_usable_lba;
|
||||
kutil::guid disk_guid;
|
||||
uint64_t table_lba;
|
||||
uint32_t entry_count;
|
||||
uint32_t entry_length;
|
||||
uint32_t table_crc32;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gpt_entry
|
||||
{
|
||||
kutil::guid type;
|
||||
kutil::guid part_guid;
|
||||
uint64_t start_lba;
|
||||
uint64_t end_lba;
|
||||
uint64_t attributes;
|
||||
uint16_t name_wide[36];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
partition::partition(block_device *parent, size_t start, size_t length) :
|
||||
m_parent(parent),
|
||||
m_start(start),
|
||||
m_length(length)
|
||||
{
|
||||
}
|
||||
|
||||
size_t
|
||||
partition::read(size_t offset, size_t length, void *buffer)
|
||||
{
|
||||
if (offset + length > m_length)
|
||||
offset = m_length - offset;
|
||||
return m_parent->read(m_start + offset, length, buffer);
|
||||
}
|
||||
|
||||
|
||||
unsigned
|
||||
partition::load(block_device *device)
|
||||
{
|
||||
// Read LBA 1
|
||||
uint8_t block[block_size];
|
||||
size_t count = device->read(block_size, block_size, &block);
|
||||
kassert(count == block_size, "Short read for GPT header.");
|
||||
|
||||
gpt_header *header = reinterpret_cast<gpt_header *>(&block);
|
||||
if (header->signature != gpt_signature)
|
||||
return 0;
|
||||
|
||||
size_t arraysize = header->entry_length * header->entry_count;
|
||||
log::debug(logs::fs, "Found GPT header: %d paritions, size 0x%lx",
|
||||
header->entry_count, arraysize);
|
||||
|
||||
uint8_t *array = new uint8_t[arraysize];
|
||||
count = device->read(block_size * header->table_lba, arraysize, array);
|
||||
kassert(count == arraysize, "Short read for GPT entry array.");
|
||||
|
||||
auto &dm = device_manager::get();
|
||||
|
||||
unsigned found = 0;
|
||||
gpt_entry *entry0 = reinterpret_cast<gpt_entry *>(array);
|
||||
for (uint32_t i = 0; i < header->entry_count; ++i) {
|
||||
gpt_entry *entry = kutil::offset_pointer(entry0, i * header->entry_length);
|
||||
if (entry->type == efi_unused_part) continue;
|
||||
|
||||
// TODO: real UTF16->UTF8
|
||||
char name[sizeof(gpt_entry::name_wide) / 2];
|
||||
for (int i = 0; i < sizeof(name); ++i)
|
||||
name[i] = entry->name_wide[i];
|
||||
|
||||
log::debug(logs::fs, "Found partition %02x at %lx-%lx", i, entry->start_lba, entry->end_lba);
|
||||
if (entry->type == efi_system_part)
|
||||
log::debug(logs::fs, " type EFI SYSTEM PARTITION");
|
||||
else
|
||||
log::debug(logs::fs, " type %G", entry->type);
|
||||
log::debug(logs::fs, " name %s", name);
|
||||
log::debug(logs::fs, " attr %016lx", entry->attributes);
|
||||
|
||||
found += 1;
|
||||
partition *part = new partition(
|
||||
device,
|
||||
entry->start_lba * block_size,
|
||||
(entry->end_lba - entry->start_lba) * block_size);
|
||||
dm.register_block_device(part);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
} // namespace fs
|
||||
37
src/kernel/fs/gpt.h
Normal file
37
src/kernel/fs/gpt.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
/// \file gpt.h
|
||||
/// Definitions for dealing with GUID Partition Tables
|
||||
#include "block_device.h"
|
||||
|
||||
namespace fs {
|
||||
|
||||
|
||||
class partition :
|
||||
public block_device
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg parent The block device this partition is a part of
|
||||
/// \arg start The starting offset in bytes from the start of the parent
|
||||
/// \arg lenght The length in bytes of this partition
|
||||
partition(block_device *parent, size_t start, size_t length);
|
||||
|
||||
/// Read bytes from the partition.
|
||||
/// \arg offset The offset in bytes at which to start reading
|
||||
/// \arg length The number of bytes to read
|
||||
/// \arg buffer [out] Data is read into this buffer
|
||||
/// \returns The number of bytes read
|
||||
virtual size_t read(size_t offset, size_t length, void *buffer);
|
||||
|
||||
/// Find partitions on a block device and add them to the device manager
|
||||
/// \arg device The device to search for partitions
|
||||
/// \returns The number of partitions found
|
||||
static unsigned load(block_device *device);
|
||||
|
||||
private:
|
||||
block_device *m_parent;
|
||||
size_t m_start;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
Reference in New Issue
Block a user