Kernel: Cleanup GPT parsing code

This commit is contained in:
Bananymous 2023-03-23 11:13:14 +02:00
parent 2ec18855f2
commit 96579b88cf
4 changed files with 84 additions and 90 deletions

View File

@ -3,15 +3,12 @@
#include <stddef.h>
#include <stdint.h>
namespace BAN
namespace BAN::UTF8
{
namespace UTF8
{
static constexpr uint32_t invalid = 0xFFFFFFFF;
}
static constexpr uint32_t utf8_byte_length(uint8_t first_byte)
constexpr uint32_t byte_length(uint8_t first_byte)
{
if ((first_byte & 0x80) == 0x00)
return 1;
@ -24,9 +21,9 @@ namespace BAN
return 0;
}
static constexpr uint32_t utf8_to_codepoint(uint8_t* bytes)
constexpr uint32_t to_codepoint(uint8_t* bytes)
{
uint32_t length = utf8_byte_length(bytes[0]);
uint32_t length = byte_length(bytes[0]);
for (uint32_t i = 1; i < length; i++)
if ((bytes[i] & 0xC0) != 0x80)
@ -43,4 +40,42 @@ namespace BAN
return UTF8::invalid;
}
template<typename T>
constexpr bool from_codepoints(const T* codepoints, size_t count, char* out)
{
uint8_t* ptr = (uint8_t*)out;
for (size_t i = 0; i < count; i++)
{
if (codepoints[i] < 0x80)
{
*ptr++ = codepoints[i];
}
else if (codepoints[i] < 0x800)
{
*ptr++ = 0xC0 | ((codepoints[i] >> 6) & 0x1F);
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
}
else if (codepoints[i] < 0x10000)
{
*ptr++ = 0xE0 | ((codepoints[i] >> 12) & 0x0F);
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
}
else if (codepoints[i] < 0x110000)
{
*ptr++ = 0xF0 | ((codepoints[i] >> 18) & 0x07);
*ptr++ = 0x80 | ((codepoints[i] >> 12) & 0x3F);
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
}
else
{
return false;
}
}
return true;
}
}

View File

@ -38,7 +38,7 @@ namespace Kernel
const uint64_t m_lba_start;
const uint64_t m_lba_end;
const uint64_t m_attributes;
char m_name[36 * 3 + 1];
char m_name[36 * 4 + 1];
};
public:

View File

@ -202,7 +202,7 @@ namespace Kernel
ASSERT(byte_index < 4);
bytes[byte_index++] = byte;
uint32_t len = BAN::utf8_byte_length(bytes[0]);
uint32_t len = BAN::UTF8::byte_length(bytes[0]);
if (len == 0)
{
@ -211,7 +211,7 @@ namespace Kernel
}
else if (len == byte_index)
{
uint32_t codepoint = BAN::utf8_to_codepoint(bytes);
uint32_t codepoint = BAN::UTF8::to_codepoint(bytes);
if (codepoint == BAN::UTF8::invalid)
invalid_utf = true;
else if (glyph_offsets.contains(codepoint))

View File

@ -1,5 +1,7 @@
#include <BAN/Endianness.h>
#include <BAN/ScopeGuard.h>
#include <BAN/StringView.h>
#include <BAN/UTF8.h>
#include <kernel/FS/Ext2.h>
#include <kernel/FS/VirtualFileSystem.h>
#include <kernel/PCI.h>
@ -15,20 +17,32 @@ namespace Kernel
struct GPTHeader
{
char signature[8];
uint32_t revision;
uint32_t size;
uint32_t crc32;
uint64_t my_lba;
uint64_t first_lba;
uint64_t last_lba;
GUID guid;
uint64_t partition_entry_lba;
uint32_t partition_entry_count;
uint32_t partition_entry_size;
uint32_t partition_entry_array_crc32;
BAN::LittleEndian<uint32_t> revision;
BAN::LittleEndian<uint32_t> size;
BAN::LittleEndian<uint32_t> crc32;
BAN::LittleEndian<uint32_t> reserved;
BAN::LittleEndian<uint64_t> my_lba;
BAN::LittleEndian<uint64_t> alternate_lba;
BAN::LittleEndian<uint64_t> first_usable_lba;
BAN::LittleEndian<uint64_t> last_usable_lba;
GUID disk_guid;
BAN::LittleEndian<uint64_t> partition_entry_lba;
BAN::LittleEndian<uint32_t> partition_entry_count;
BAN::LittleEndian<uint32_t> partition_entry_size;
BAN::LittleEndian<uint32_t> partition_entry_array_crc32;
};
uint32_t crc32_table[256] =
struct PartitionEntry
{
GUID partition_type_guid;
GUID unique_partition_guid;
BAN::LittleEndian<uint64_t> starting_lba;
BAN::LittleEndian<uint64_t> ending_lba;
BAN::LittleEndian<uint64_t> attributes;
BAN::LittleEndian<uint16_t> partition_name[36];
} __attribute__((packed));
static uint32_t crc32_table[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
@ -107,16 +121,6 @@ namespace Kernel
return crc32 ^ 0xFFFFFFFF;
}
static GUID parse_guid(const uint8_t* guid)
{
GUID result;
result.data1 = BAN::Math::big_endian_to_host<uint32_t>(guid + 0);
result.data2 = BAN::Math::big_endian_to_host<uint16_t>(guid + 4);
result.data3 = BAN::Math::big_endian_to_host<uint16_t>(guid + 6);
memcpy(result.data4, guid + 8, 8);
return result;
}
static bool is_valid_gpt_header(const GPTHeader& header, uint32_t sector_size)
{
if (memcmp(header.signature, "EFI PART", 8) != 0)
@ -140,58 +144,12 @@ namespace Kernel
return true;
}
static GPTHeader parse_gpt_header(const BAN::Vector<uint8_t>& lba1)
{
GPTHeader header;
memset(&header, 0, sizeof(header));
memcpy(header.signature, lba1.data(), 8);
header.revision = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 8);
header.size = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 12);
header.crc32 = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 16);
header.my_lba = BAN::Math::little_endian_to_host<uint64_t>(lba1.data() + 24);
header.first_lba = BAN::Math::little_endian_to_host<uint64_t>(lba1.data() + 40);
header.last_lba = BAN::Math::little_endian_to_host<uint64_t>(lba1.data() + 48);
header.guid = parse_guid(lba1.data() + 56);
header.partition_entry_lba = BAN::Math::little_endian_to_host<uint64_t>(lba1.data() + 72);
header.partition_entry_count = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 80);
header.partition_entry_size = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 84);
header.partition_entry_array_crc32 = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 88);
return header;
}
static void utf8_encode(const uint16_t* codepoints, size_t count, char* out)
{
uint32_t len = 0;
while (*codepoints && count--)
{
uint16_t cp = *codepoints;
if (cp < 128)
{
out[len++] = cp & 0x7F;
}
else if (cp < 2048)
{
out[len++] = 0xC0 | ((cp >> 0x6) & 0x1F);
out[len++] = 0x80 | (cp & 0x3F);
}
else
{
out[len++] = 0xE0 | ((cp >> 0xC) & 0x0F);
out[len++] = 0x80 | ((cp >> 0x6) & 0x3F);
out[len++] = 0x80 | (cp & 0x3F);
}
codepoints++;
}
out[len] = 0;
}
BAN::ErrorOr<void> StorageDevice::initialize_partitions()
{
BAN::Vector<uint8_t> lba1(sector_size());
TRY(read_sectors(1, 1, lba1.data()));
GPTHeader header = parse_gpt_header(lba1);
const GPTHeader& header = *(const GPTHeader*)lba1.data();
if (!is_valid_gpt_header(header, sector_size()))
return BAN::Error::from_c_string("Invalid GPT header");
@ -199,7 +157,8 @@ namespace Kernel
if (uint32_t remainder = size % sector_size())
size += sector_size() - remainder;
BAN::Vector<uint8_t> entry_array(size);
BAN::Vector<uint8_t> entry_array;
TRY(entry_array.resize(size));
TRY(read_sectors(header.partition_entry_lba, size / sector_size(), entry_array.data()));
if (!is_valid_gpt_crc32(header, lba1, entry_array))
@ -207,18 +166,18 @@ namespace Kernel
for (uint32_t i = 0; i < header.partition_entry_count; i++)
{
uint8_t* partition_data = entry_array.data() + header.partition_entry_size * i;
const PartitionEntry& entry = *(const PartitionEntry*)(entry_array.data() + header.partition_entry_size * i);
char utf8_name[36 * 3 + 1]; // 36 16-bit codepoints + nullbyte
utf8_encode((uint16_t*)(partition_data + 56), header.partition_entry_size - 56, utf8_name);
char utf8_name[36 * 4 + 1];
BAN::UTF8::from_codepoints(entry.partition_name, 36, utf8_name);
MUST(m_partitions.emplace_back(
*this,
parse_guid(partition_data + 0),
parse_guid(partition_data + 16),
BAN::Math::little_endian_to_host<uint64_t>(partition_data + 32),
BAN::Math::little_endian_to_host<uint64_t>(partition_data + 40),
BAN::Math::little_endian_to_host<uint64_t>(partition_data + 48),
entry.partition_type_guid,
entry.unique_partition_guid,
entry.starting_lba,
entry.ending_lba,
entry.attributes,
utf8_name
));
}