285 lines
7.1 KiB
C++
285 lines
7.1 KiB
C++
/*
|
|
* \brief GUID Partition table definitions
|
|
* \author Josef Soentgen
|
|
* \date 2014-09-19
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2014 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#ifndef _PART_BLK__GPT_H_
|
|
#define _PART_BLK__GPT_H_
|
|
|
|
#include <base/env.h>
|
|
#include <base/printf.h>
|
|
#include <block_session/client.h>
|
|
|
|
#include "driver.h"
|
|
#include "partition_table.h"
|
|
|
|
namespace {
|
|
|
|
static bool const verbose = false;
|
|
#define PLOGV(...) do { if (verbose) PLOG(__VA_ARGS__); } while (0)
|
|
|
|
/* simple bitwise CRC32 checking */
|
|
static inline Genode::uint32_t crc32(void const * const buf, Genode::size_t size)
|
|
{
|
|
Genode::uint8_t const *p = static_cast<Genode::uint8_t const*>(buf);
|
|
Genode::uint32_t crc = ~0U;
|
|
|
|
while (size--) {
|
|
crc ^= *p++;
|
|
for (Genode::uint32_t j = 0; j < 8; j++)
|
|
crc = (-Genode::int32_t(crc & 1) & 0xedb88320) ^ (crc >> 1);
|
|
}
|
|
|
|
return crc ^ ~0U;
|
|
}
|
|
|
|
} /* anonymous namespace */
|
|
|
|
|
|
class Gpt : public Block::Partition_table
|
|
{
|
|
private:
|
|
|
|
enum { MAX_PARTITIONS = 128 };
|
|
|
|
/* contains pointers to valid partitions or 0 */
|
|
Block::Partition *_part_list[MAX_PARTITIONS] { 0 };
|
|
|
|
typedef Block::Partition_table::Sector Sector;
|
|
|
|
|
|
/**
|
|
* DCE uuid struct
|
|
*/
|
|
struct Uuid
|
|
{
|
|
enum { UUID_NODE_LEN = 6 };
|
|
|
|
Genode::uint32_t time_low;
|
|
Genode::uint16_t time_mid;
|
|
Genode::uint16_t time_hi_and_version;
|
|
Genode::uint8_t clock_seq_hi_and_reserved;
|
|
Genode::uint8_t clock_seq_low;
|
|
Genode::uint8_t node[UUID_NODE_LEN];
|
|
|
|
char const *to_string()
|
|
{
|
|
static char buffer[37 + 1];
|
|
|
|
Genode::snprintf(buffer, sizeof(buffer),
|
|
"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
|
time_low, time_mid, time_hi_and_version,
|
|
clock_seq_hi_and_reserved, clock_seq_low,
|
|
node[0], node[1], node[2], node[3], node[4], node[5]);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
/**
|
|
* GUID parition table header
|
|
*/
|
|
struct Gpt_hdr
|
|
{
|
|
enum { HEADER_LBA = 1 };
|
|
|
|
char _sig[8]; /* identifies GUID Partition Table */
|
|
Genode::uint32_t _revision; /* GPT specification revision */
|
|
Genode::uint32_t _hdr_size; /* size of GPT header */
|
|
Genode::uint32_t _hdr_crc; /* CRC32 of GPT header */
|
|
Genode::uint32_t _reserved; /* must be zero */
|
|
Genode::uint64_t _hdr_lba; /* LBA that contains this header */
|
|
Genode::uint64_t _backup_hdr_lba; /* LBA of backup GPT header */
|
|
Genode::uint64_t _part_lba_start; /* first LBA usable for partitions */
|
|
Genode::uint64_t _part_lba_end; /* last LBA usable for partitions */
|
|
Uuid _guid; /* GUID to identify the disk */
|
|
Genode::uint64_t _gpe_lba; /* first LBA of GPE array */
|
|
Genode::uint32_t _entries; /* number of entries in GPE array */
|
|
Genode::uint32_t _entry_size; /* size of each GPE */
|
|
Genode::uint32_t _gpe_crc; /* CRC32 of GPE array */
|
|
|
|
void dump_hdr(bool check_primary)
|
|
{
|
|
PLOGV("GPT %s header:", check_primary ? "primary" : "backup");
|
|
PLOGV(" rev: %u", _revision);
|
|
PLOGV(" size: %u", _hdr_size);
|
|
PLOGV(" crc: %x", _hdr_crc);
|
|
PLOGV(" reserved: %u", _reserved);
|
|
PLOGV(" hdr lba: %llu", _hdr_lba);
|
|
PLOGV(" bak lba: %llu", _backup_hdr_lba);
|
|
PLOGV(" part start lba: %llu", _part_lba_start);
|
|
PLOGV(" part end lba: %llu", _part_lba_end);
|
|
PLOGV(" guid: %s", _guid.to_string());
|
|
PLOGV(" gpe lba: %llu", _gpe_lba);
|
|
PLOGV(" entries: %u", _entries);
|
|
PLOGV(" entry size: %u", _entry_size);
|
|
PLOGV(" gpe crc: %x", _gpe_crc);
|
|
}
|
|
|
|
bool is_valid(bool check_primary = true)
|
|
{
|
|
dump_hdr(check_primary);
|
|
|
|
/* check sig */
|
|
if (Genode::strcmp(_sig, "EFI PART", 8) != 0)
|
|
return false;
|
|
|
|
/* check header crc */
|
|
Genode::uint32_t crc = _hdr_crc;
|
|
_hdr_crc = 0;
|
|
if (crc32(this, _hdr_size) != crc) {
|
|
PERR("Wrong GPT header checksum");
|
|
return false;
|
|
}
|
|
|
|
/* check header lba */
|
|
if (check_primary)
|
|
if (_hdr_lba != HEADER_LBA)
|
|
return false;
|
|
|
|
/* check GPT entry array */
|
|
Genode::size_t length = _entries * _entry_size;
|
|
Sector gpe(_gpe_lba, length / Block::Driver::driver().blk_size());
|
|
if (crc32(gpe.addr<void *>(), length) != _gpe_crc)
|
|
return false;
|
|
|
|
if (check_primary) {
|
|
/* check backup gpt header */
|
|
Sector backup_hdr(_backup_hdr_lba, 1);
|
|
if (!backup_hdr.addr<Gpt_hdr*>()->is_valid(false)) {
|
|
PWRN("Backup GPT header is corrupted");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* the remainder of the LBA must be zero */
|
|
} __attribute__((packed));
|
|
|
|
|
|
/**
|
|
* GUID partition entry format
|
|
*/
|
|
struct Gpt_entry
|
|
{
|
|
enum { NAME_LEN = 36 };
|
|
Uuid _type; /* partition type GUID */
|
|
Uuid _guid; /* unique partition GUID */
|
|
Genode::uint64_t _lba_start; /* start of partition */
|
|
Genode::uint64_t _lba_end; /* end of partition */
|
|
Genode::uint64_t _attr; /* partition attributes */
|
|
Genode::uint16_t _name[NAME_LEN]; /* partition name in UNICODE-16 */
|
|
|
|
bool is_valid()
|
|
{
|
|
if (_type.time_low == 0x00000000)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Extract all valid ASCII characters in the name entry
|
|
*/
|
|
char const *name()
|
|
{
|
|
static char buffer[NAME_LEN + 1];
|
|
|
|
char *p = buffer;
|
|
Genode::size_t i = 0;
|
|
|
|
for (Genode::size_t u = 0; u < NAME_LEN && _name[u] != 0; u++) {
|
|
Genode::uint32_t utfchar = _name[i++];
|
|
|
|
if ((utfchar & 0xf800) == 0xd800) {
|
|
unsigned int c = _name[i];
|
|
if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
|
|
utfchar = 0xfffd;
|
|
else
|
|
i++;
|
|
}
|
|
|
|
*p++ = (utfchar < 0x80) ? utfchar : '.';
|
|
}
|
|
|
|
*p++ = 0;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
/**
|
|
* Parse the GPT header
|
|
*/
|
|
void _parse_gpt(Gpt_hdr *gpt)
|
|
{
|
|
if (!(gpt->is_valid()))
|
|
throw Genode::Exception();
|
|
|
|
Sector entry_array(gpt->_gpe_lba,
|
|
gpt->_entries * gpt->_entry_size / Block::Driver::driver().blk_size());
|
|
Gpt_entry *entries = entry_array.addr<Gpt_entry *>();
|
|
|
|
for (int i = 0; i < MAX_PARTITIONS; i++) {
|
|
Gpt_entry *e = (entries + i);
|
|
|
|
if (!e->is_valid())
|
|
continue;
|
|
|
|
Genode::uint64_t start = e->_lba_start;
|
|
Genode::uint64_t length = e->_lba_end - e->_lba_start + 1; /* [...) */
|
|
|
|
_part_list[i] = new (Genode::env()->heap()) Block::Partition(start, length);
|
|
|
|
PINF("Partition %d: LBA %llu (%llu blocks) type: '%s' name: '%s'",
|
|
i + 1, start, length, e->_type.to_string(), e->name());
|
|
}
|
|
}
|
|
|
|
Gpt()
|
|
{
|
|
Sector s(Gpt_hdr::HEADER_LBA, 1);
|
|
_parse_gpt(s.addr<Gpt_hdr *>());
|
|
|
|
/*
|
|
* we read all partition information,
|
|
* now it's safe to turn in asynchronous mode
|
|
*/
|
|
Block::Driver::driver().work_asynchronously();
|
|
}
|
|
|
|
public:
|
|
|
|
Block::Partition *partition(int num) {
|
|
return (num <= MAX_PARTITIONS && num > 0) ? _part_list[num-1] : 0; }
|
|
|
|
bool avail()
|
|
{
|
|
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
|
|
if (_part_list[num])
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static Gpt& table()
|
|
{
|
|
static Gpt table;
|
|
return table;
|
|
}
|
|
};
|
|
|
|
#endif /* _PART_BLK__GUID_PARTITION_TABLE_H_ */
|