diff --git a/repos/os/src/server/part_blk/README b/repos/os/src/server/part_blk/README index 291439247..9ebb01169 100644 --- a/repos/os/src/server/part_blk/README +++ b/repos/os/src/server/part_blk/README @@ -14,6 +14,10 @@ extended boot records (EBRs) are parsed and offered as separate block sessions to the front-end clients. The four primary partitions will receive partition numbers '1' to '4' whereas the first logical partition will be assigned to '5'. +The partition server also understands the GUID partition table (GPT). If the +config attribute 'use_gpt' is set to 'yes' it will first try to parse any +existing GPT. In case there is no GPT it will fall back to parsing the MBR. + In order to route a client to the right partition, the server parses its configuration section looking for 'policy' tags. diff --git a/repos/os/src/server/part_blk/component.h b/repos/os/src/server/part_blk/component.h index 20bb99191..4db369d0a 100644 --- a/repos/os/src/server/part_blk/component.h +++ b/repos/os/src/server/part_blk/component.h @@ -18,7 +18,7 @@ #include #include -#include "partition_table.h" +#include "gpt.h" namespace Block { @@ -195,8 +195,9 @@ class Block::Root : { private: - Rpc_entrypoint &_ep; - Signal_receiver &_receiver; + Rpc_entrypoint &_ep; + Signal_receiver &_receiver; + Block::Partition_table &_table; long _partition_num(const char *session_label) { @@ -265,7 +266,7 @@ class Block::Root : throw Root::Invalid_args(); } - if (!Partition_table::table().partition(num)) { + if (!_table.partition(num)) { PERR("Partition %ld unavailable", num); throw Root::Unavailable(); } @@ -274,18 +275,19 @@ class Block::Root : ds_cap = Genode::env()->ram_session()->alloc(tx_buf_size); return new (md_alloc()) Session_component(ds_cap, - Partition_table::table().partition(num), + _table.partition(num), _ep, _receiver); } public: Root(Rpc_entrypoint *session_ep, Allocator *md_alloc, - Signal_receiver &receiver) + Signal_receiver &receiver, Block::Partition_table& table) : Root_component(session_ep, md_alloc), _ep(*session_ep), - _receiver(receiver) + _receiver(receiver), + _table(table) { } }; diff --git a/repos/os/src/server/part_blk/gpt.h b/repos/os/src/server/part_blk/gpt.h new file mode 100644 index 000000000..3e5a62455 --- /dev/null +++ b/repos/os/src/server/part_blk/gpt.h @@ -0,0 +1,284 @@ +/* + * \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_ */ diff --git a/repos/os/src/server/part_blk/main.cc b/repos/os/src/server/part_blk/main.cc index f644a3a18..2dca16845 100644 --- a/repos/os/src/server/part_blk/main.cc +++ b/repos/os/src/server/part_blk/main.cc @@ -2,11 +2,12 @@ * \brief Front end of the partition server * \author Sebastian Sumpf * \author Stefan Kalkowski + * \author Josef Soentgen * \date 2011-05-30 */ /* - * Copyright (C) 2011-2013 Genode Labs GmbH + * Copyright (C) 2011-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. @@ -16,9 +17,10 @@ #include #include -#include "driver.h" -#include "partition_table.h" #include "component.h" +#include "driver.h" +#include "gpt.h" +#include "mbr.h" static Genode::Signal_receiver receiver; @@ -33,19 +35,48 @@ void Block::Driver::_ready_to_submit(unsigned) { Block::Session_component::wake_up(); } +static bool _use_gpt() +{ + try { + return Genode::config()->xml_node().attribute("use_gpt").has_value("yes"); + } catch(...) { } + + return false; +} + + int main() { using namespace Genode; - if (!Block::Partition_table::table().avail()) { - PERR("No valid partition table found"); - return 1; + bool valid_mbr = false; + bool valid_gpt = false; + bool use_gpt = _use_gpt(); + + if (use_gpt) + try { valid_gpt = Gpt::table().avail(); } catch (...) { } + + /* fall back to MBR */ + if (!valid_gpt) { + try { valid_mbr = Mbr_partition_table::table().avail(); } + catch (Mbr_partition_table::Protective_mbr_found) { + if (!use_gpt) + PERR("Aborting: found protective MBR but GPT usage was not requested."); + return 1; + } } + Block::Partition_table *partition_table = 0; + if (valid_gpt) + partition_table = &Gpt::table(); + if (valid_mbr) + partition_table = &Mbr_partition_table::table(); + enum { STACK_SIZE = 1024 * sizeof(Genode::size_t) }; static Cap_connection cap; static Rpc_entrypoint ep(&cap, STACK_SIZE, "part_ep"); - static Block::Root block_root(&ep, env()->heap(), receiver); + static Block::Root block_root(&ep, env()->heap(), receiver, + *partition_table); env()->parent()->announce(ep.manage(&block_root)); diff --git a/repos/os/src/server/part_blk/mbr.h b/repos/os/src/server/part_blk/mbr.h new file mode 100644 index 000000000..6f0c65c94 --- /dev/null +++ b/repos/os/src/server/part_blk/mbr.h @@ -0,0 +1,174 @@ +/* + * \brief MBR partition table definitions + * \author Sebastian Sumpf + * \author Stefan Kalkowski + * \author Josef Soentgen + * \date 2013-12-04 + */ + +/* + * Copyright (C) 2013-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__MBR_H_ +#define _PART_BLK__MBR_H_ + +#include +#include +#include + +#include "driver.h" +#include "partition_table.h" + + +struct Mbr_partition_table : public Block::Partition_table +{ + public: + + class Protective_mbr_found { }; + + private: + + typedef Block::Partition_table::Sector Sector; + + /** + * Partition table entry format + */ + struct Partition_record + { + enum { INVALID = 0, EXTENTED = 0x5, PROTECTIVE = 0xee, }; + Genode::uint8_t _unused[4]; + Genode::uint8_t _type; /* partition type */ + Genode::uint8_t _unused2[3]; + Genode::uint32_t _lba; /* logical block address */ + Genode::uint32_t _sectors; /* number of sectors */ + + bool is_valid() { return _type != INVALID; } + bool is_extented() { return _type == EXTENTED; } + bool is_protective() { return _type == PROTECTIVE; } + } __attribute__((packed)); + + + /** + * Master/Extented boot record format + */ + struct Mbr + { + Genode::uint8_t _unused[446]; + Partition_record _records[4]; + Genode::uint16_t _magic; + + bool is_valid() + { + /* magic number of partition table */ + enum { MAGIC = 0xaa55 }; + return _magic == MAGIC; + } + } __attribute__((packed)); + + + enum { MAX_PARTITIONS = 32 }; + + Block::Partition *_part_list[MAX_PARTITIONS]; /* contains pointers to valid + partitions or 0 */ + + void _parse_extented(Partition_record *record) + { + Partition_record *r = record; + unsigned lba = r->_lba; + + /* first logical partition number */ + int nr = 5; + do { + Sector s(lba, 1); + Mbr *ebr = s.addr(); + + if (!(ebr->is_valid())) + return; + + /* The first record is the actual logical partition. The lba of this + * partition is relative to the lba of the current EBR */ + Partition_record *logical = &(ebr->_records[0]); + if (logical->is_valid() && nr < MAX_PARTITIONS) { + _part_list[nr++] = new (Genode::env()->heap()) + Block::Partition(logical->_lba + lba, logical->_sectors); + + PINF("Partition %d: LBA %u (%u blocks) type %x", nr - 1, + logical->_lba + lba, logical->_sectors, logical->_type); + } + + /* + * the second record points to the next EBR + * (relative form this EBR) + */ + r = &(ebr->_records[1]); + lba += ebr->_records[1]._lba; + + } while (r->is_valid()); + } + + void _parse_mbr(Mbr *mbr) + { + /* no partition table, use whole disc as partition 0 */ + if (!(mbr->is_valid())) + _part_list[0] = new(Genode::env()->heap()) + Block::Partition(0, Block::Driver::driver().blk_cnt() - 1); + + for (int i = 0; i < 4; i++) { + Partition_record *r = &(mbr->_records[i]); + + if (!r->is_valid()) + continue; + + PINF("Partition %d: LBA %u (%u blocks) type: %x", + i + 1, r->_lba, r->_sectors, r->_type); + + if (r->is_protective()) + throw Protective_mbr_found(); + + if (r->is_extented()) { + _parse_extented(r); + continue; + } + + _part_list[i + 1] = new(Genode::env()->heap()) + Block::Partition(r->_lba, r->_sectors); + } + } + + Mbr_partition_table() + { + Sector s(0, 1); + _parse_mbr(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) ? _part_list[num] : 0; } + + bool avail() + { + for (unsigned num = 0; num < MAX_PARTITIONS; num++) + if (_part_list[num]) + return true; + return false; + } + + static Mbr_partition_table& table() + { + static Mbr_partition_table table; + return table; + } +}; + +#endif /* _PART_BLK__MBR_H_ */ diff --git a/repos/os/src/server/part_blk/partition_table.h b/repos/os/src/server/part_blk/partition_table.h index 539adbe48..d03aecfe0 100644 --- a/repos/os/src/server/part_blk/partition_table.h +++ b/repos/os/src/server/part_blk/partition_table.h @@ -29,18 +29,16 @@ namespace Block { struct Block::Partition { - Genode::uint32_t lba; /* logical block address on device */ - Genode::uint32_t sectors; /* number of sectors in patitions */ + Genode::uint64_t lba; /* logical block address on device */ + Genode::uint64_t sectors; /* number of sectors in patitions */ - Partition(Genode::uint32_t l, Genode::uint32_t s) + Partition(Genode::uint64_t l, Genode::uint64_t s) : lba(l), sectors(s) { } }; -class Block::Partition_table +struct Block::Partition_table { - private: - class Sector { private: @@ -70,137 +68,9 @@ class Block::Partition_table return reinterpret_cast(_session.tx()->packet_content(_p)); } }; + virtual Partition *partition(int num) = 0; - /** - * Partition table entry format - */ - struct Partition_record - { - enum { INVALID = 0, EXTENTED = 0x5 }; - Genode::uint8_t _unused[4]; - Genode::uint8_t _type; /* partition type */ - Genode::uint8_t _unused2[3]; - Genode::uint32_t _lba; /* logical block address */ - Genode::uint32_t _sectors; /* number of sectors */ - - bool is_valid() { return _type != INVALID; } - bool is_extented() { return _type == EXTENTED; } - } __attribute__((packed)); - - - /** - * Master/Extented boot record format - */ - struct Mbr - { - Genode::uint8_t _unused[446]; - Partition_record _records[4]; - Genode::uint16_t _magic; - - bool is_valid() - { - /* magic number of partition table */ - enum { MAGIC = 0xaa55 }; - return _magic == MAGIC; - } - } __attribute__((packed)); - - - enum { MAX_PARTITIONS = 32 }; - - Partition *_part_list[MAX_PARTITIONS]; /* contains pointers to valid - partitions or 0 */ - - void _parse_extented(Partition_record *record) - { - Partition_record *r = record; - unsigned lba = r->_lba; - - /* first logical partition number */ - int nr = 5; - do { - Sector s(lba, 1); - Mbr *ebr = s.addr(); - - if (!(ebr->is_valid())) - return; - - /* The first record is the actual logical partition. The lba of this - * partition is relative to the lba of the current EBR */ - Partition_record *logical = &(ebr->_records[0]); - if (logical->is_valid() && nr < MAX_PARTITIONS) { - _part_list[nr++] = new (Genode::env()->heap()) - Partition(logical->_lba + lba, logical->_sectors); - - PINF("Partition %d: LBA %u (%u blocks) type %x", nr - 1, - logical->_lba + lba, logical->_sectors, logical->_type); - } - - /* - * the second record points to the next EBR - * (relative form this EBR) - */ - r = &(ebr->_records[1]); - lba += ebr->_records[1]._lba; - - } while (r->is_valid()); - } - - - void _parse_mbr(Mbr *mbr) - { - /* no partition table, use whole disc as partition 0 */ - if (!(mbr->is_valid())) - _part_list[0] = new(Genode::env()->heap()) - Partition(0, Driver::driver().blk_cnt() - 1); - - for (int i = 0; i < 4; i++) { - Partition_record *r = &(mbr->_records[i]); - - if (r->is_valid()) - PINF("Partition %d: LBA %u (%u blocks) type: %x", - i + 1, r->_lba, r->_sectors, r->_type); - else - continue; - - if (r->is_extented()) - _parse_extented(r); - else - _part_list[i + 1] = new(Genode::env()->heap()) - Partition(r->_lba, r->_sectors); - } - } - - Partition_table() - { - Sector s(0, 1); - _parse_mbr(s.addr()); - - /* - * we read all partition information, - * now it's safe to turn in asynchronous mode - */ - Driver::driver().work_asynchronously(); - } - - public: - - Partition *partition(int num) { - return (num < MAX_PARTITIONS) ? _part_list[num] : 0; } - - bool avail() - { - for (unsigned num = 0; num < MAX_PARTITIONS; num++) - if (_part_list[num]) - return true; - return false; - } - - static Partition_table& table() - { - static Partition_table table; - return table; - } + virtual bool avail() = 0; }; #endif /* _PART_BLK__PARTITION_TABLE_H_ */