/* * \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 #include #include #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(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(), length) != _gpe_crc) return false; if (check_primary) { /* check backup gpt header */ Sector backup_hdr(_backup_hdr_lba, 1); if (!backup_hdr.addr()->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(); 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()); /* * 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_ */