part_block: support for AHDI partition scheme

This patch enhances part_block with support for parsing the AHDI
partition scheme, and the detection of the GEMDOS variant of FAT as used
by Atari TOS. As a side effect of the implementation, the patch improves
the MBR parsing code by avoiding pointers and using const qualifiers.

Fixes #3470
This commit is contained in:
Norman Feske 2019-08-09 16:45:51 +02:00 committed by Christian Helmuth
parent 23c2606ce0
commit ceae637416
5 changed files with 215 additions and 92 deletions

View File

@ -0,0 +1,98 @@
/*
* \brief Atari ST partition scheme (AHDI)
* \author Norman Feske
* \date 2019-08-09
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _PART_BLOCK__AHDI_H_
#define _PART_BLOCK__AHDI_H_
#include "partition_table.h"
struct Ahdi
{
typedef Block::Partition_table::Sector Sector;
typedef Genode::uint32_t uint32_t;
typedef Genode::uint8_t uint8_t;
/**
* 32-bit big-endian value
*/
struct Be32
{
uint8_t b0, b1, b2, b3;
uint32_t value() const {
return ((uint32_t)b0 << 24) | ((uint32_t)b1 << 16)
| ((uint32_t)b2 << 8) | ((uint32_t)b3 << 0); }
} __attribute__((packed));
struct Partition_record
{
uint8_t _flags;
uint8_t _id0, _id1, _id2;
Be32 start; /* first block */
Be32 length; /* in blocks */
typedef Genode::String<4> Id;
Id id() const
{
using Genode::Char;
return Id(Char(_id0), Char(_id1), Char(_id2));
}
bool bootable() const { return _flags & 1; }
bool valid() const { return id() == "BGM" && start.value() > 0; }
} __attribute__((packed));
enum { MAX_PARTITIONS = 4 };
struct Root_sector
{
uint8_t boot_code[0x156];
Partition_record icd_partitions[8];
uint8_t unused[0xc];
Be32 disk_blocks;
Partition_record partitions[MAX_PARTITIONS];
} __attribute__((packed));
static bool valid(Sector const &sector)
{
bool any_partition_valid = false;
Root_sector const root = *sector.addr<Root_sector const *>();
for (unsigned i = 0; i < MAX_PARTITIONS; i++)
if (root.partitions[i].valid())
any_partition_valid = true;
return any_partition_valid;
}
template <typename FN>
static void for_each_partition(Sector const &sector, FN const &fn)
{
Root_sector &root = *sector.addr<Root_sector *>();
for (unsigned i = 0; i < MAX_PARTITIONS; i++) {
Partition_record const &part = root.partitions[i];
if (part.valid())
fn(i + 1, Block::Partition(part.start.value(), part.length.value()));
}
}
};
#endif /* _PART_BLOCK__AHDI_H_ */

View File

@ -69,15 +69,16 @@ namespace Fs {
if (len < 512) { return Type(); } if (len < 512) { return Type(); }
/* at least the checks ring true when mkfs.vfat is used... */ /* at least the checks ring true when mkfs.vfat is used... */
bool const found_boot_sig = p[510] == 0x55 && p[511] == 0xAA; bool const found_boot_sig = p[510] == 0x55 && p[511] == 0xAA;
bool const fat16 = p[38] == 0x28 || p[38] == 0x29; bool const fat16 = p[38] == 0x28 || p[38] == 0x29;
bool const fat32 = (p[66] == 0x28 || p[66] == 0x29) bool const fat32 = (p[66] == 0x28 || p[66] == 0x29)
&& (p[82] == 'F' && p[83] == 'A'); && (p[82] == 'F' && p[83] == 'A');
bool const gemdos = (p[0] == 0xe9);
if (found_boot_sig && fat32) { return Type("FAT32"); } if (found_boot_sig && fat32) { return Type("FAT32"); }
if (found_boot_sig && fat16) { return Type("FAT16"); } if (found_boot_sig && fat16) { return Type("FAT16"); }
if (gemdos) { return Type("GEMDOS"); }
return Type(); return Type();
} }

View File

@ -67,18 +67,16 @@ Block::Partition_table & Main::_table()
{ {
using namespace Genode; using namespace Genode;
Xml_node const config = _config.xml();
bool const ignore_gpt = config.attribute_value("ignore_gpt", false);
bool const ignore_mbr = config.attribute_value("ignore_mbr", false);
bool valid_mbr = false; bool valid_mbr = false;
bool valid_gpt = false; bool valid_gpt = false;
bool ignore_gpt = false;
bool ignore_mbr = false;
bool pmbr_found = false; bool pmbr_found = false;
bool report = false; bool report = false;
try {
ignore_gpt = _config.xml().attribute_value("ignore_gpt", false);
ignore_mbr = _config.xml().attribute_value("ignore_mbr", false);
} catch(...) {}
if (ignore_gpt && ignore_mbr) { if (ignore_gpt && ignore_mbr) {
error("invalid configuration: cannot ignore GPT as well as MBR"); error("invalid configuration: cannot ignore GPT as well as MBR");
throw Invalid_config(); throw Invalid_config();

View File

@ -3,6 +3,7 @@
* \author Sebastian Sumpf * \author Sebastian Sumpf
* \author Stefan Kalkowski * \author Stefan Kalkowski
* \author Josef Soentgen * \author Josef Soentgen
* \author Norman Feske
* \date 2013-12-04 * \date 2013-12-04
*/ */
@ -22,6 +23,7 @@
#include "partition_table.h" #include "partition_table.h"
#include "fsprobe.h" #include "fsprobe.h"
#include "ahdi.h"
struct Mbr_partition_table : public Block::Partition_table struct Mbr_partition_table : public Block::Partition_table
@ -49,10 +51,10 @@ struct Mbr_partition_table : public Block::Partition_table
Genode::uint32_t _lba; /* logical block address */ Genode::uint32_t _lba; /* logical block address */
Genode::uint32_t _sectors; /* number of sectors */ Genode::uint32_t _sectors; /* number of sectors */
bool valid() { return _type != INVALID; } bool valid() const { return _type != INVALID; }
bool extended() { return _type == EXTENTED_CHS bool extended() const { return _type == EXTENTED_CHS
|| _type == EXTENTED_LBA; } || _type == EXTENTED_LBA; }
bool protective() { return _type == PROTECTIVE; } bool protective() const { return _type == PROTECTIVE; }
} __attribute__((packed)); } __attribute__((packed));
@ -65,7 +67,7 @@ struct Mbr_partition_table : public Block::Partition_table
Partition_record _records[4]; Partition_record _records[4];
Genode::uint16_t _magic; Genode::uint16_t _magic;
bool valid() bool valid() const
{ {
/* magic number of partition table */ /* magic number of partition table */
enum { MAGIC = 0xaa55 }; enum { MAGIC = 0xaa55 };
@ -76,13 +78,13 @@ struct Mbr_partition_table : public Block::Partition_table
enum { MAX_PARTITIONS = 32 }; enum { MAX_PARTITIONS = 32 };
Block::Partition *_part_list[MAX_PARTITIONS]; /* contains pointers to valid /* contains pointers to valid partitions or 0 */
partitions or 0 */ Block::Partition *_part_list[MAX_PARTITIONS];
template <typename FUNC> template <typename FUNC>
void _parse_extended(Partition_record *record, FUNC const &f) void _parse_extended(Partition_record const &record, FUNC const &f) const
{ {
Partition_record *r = record; Partition_record const *r = &record;
unsigned lba = r->_lba; unsigned lba = r->_lba;
unsigned last_lba = 0; unsigned last_lba = 0;
@ -90,15 +92,15 @@ struct Mbr_partition_table : public Block::Partition_table
int nr = 5; int nr = 5;
do { do {
Sector s(driver, lba, 1); Sector s(driver, lba, 1);
Mbr *ebr = s.addr<Mbr *>(); Mbr const &ebr = *s.addr<Mbr *>();
if (!(ebr->valid())) if (!ebr.valid())
return; return;
/* The first record is the actual logical partition. The lba of this /* The first record is the actual logical partition. The lba of this
* partition is relative to the lba of the current EBR */ * partition is relative to the lba of the current EBR */
Partition_record *logical = &(ebr->_records[0]); Partition_record const &logical = ebr._records[0];
if (logical->valid() && nr < MAX_PARTITIONS) { if (logical.valid() && nr < MAX_PARTITIONS) {
f(nr++, logical, lba); f(nr++, logical, lba);
} }
@ -106,32 +108,30 @@ struct Mbr_partition_table : public Block::Partition_table
* the second record points to the next EBR * the second record points to the next EBR
* (relative form this EBR) * (relative form this EBR)
*/ */
r = &(ebr->_records[1]); r = &(ebr._records[1]);
lba += ebr->_records[1]._lba - last_lba; lba += ebr._records[1]._lba - last_lba;
last_lba = ebr->_records[1]._lba; last_lba = ebr._records[1]._lba;
} while (r->valid()); } while (r->valid());
} }
template <typename FUNC> template <typename FUNC>
void _parse_mbr(Mbr *mbr, FUNC const &f) void _parse_mbr(Mbr const &mbr, FUNC const &f) const
{ {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
Partition_record *r = &(mbr->_records[i]); Partition_record const &r = mbr._records[i];
if (!r->valid()) if (!r.valid())
continue; continue;
if (r->protective()) if (r.protective())
throw Protective_mbr_found(); throw Protective_mbr_found();
f(i + 1, r, 0); f(i + 1, r, 0);
if (r->extended()) { if (r.extended())
_parse_extended(r, f); _parse_extended(r, f);
}
} }
} }
@ -142,83 +142,109 @@ struct Mbr_partition_table : public Block::Partition_table
Block::Partition *partition(int num) override { Block::Partition *partition(int num) override {
return (num < MAX_PARTITIONS) ? _part_list[num] : 0; } return (num < MAX_PARTITIONS) ? _part_list[num] : 0; }
template <typename FN>
void _for_each_valid_partition(FN const &fn) const
{
for (unsigned i = 0; i < MAX_PARTITIONS; i++)
if (_part_list[i])
fn(i);
};
bool parse() override bool parse() override
{ {
Sector s(driver, 0, 1); using namespace Genode;
Mbr *mbr = s.addr<Mbr *>();
/* no partition table, use whole disc as partition 0 */ Sector s(driver, 0, 1);
bool const mbr_valid = mbr->valid();
if (!mbr_valid) { /* check for MBR */
_part_list[0] = new (&heap) Mbr const &mbr = *s.addr<Mbr *>();
Block::Partition(0, driver.blk_cnt() - 1); bool const mbr_valid = mbr.valid();
} else { if (mbr_valid) {
_parse_mbr(mbr, [&] (int i, Partition_record *r, unsigned offset) { _parse_mbr(mbr, [&] (int i, Partition_record const &r, unsigned offset) {
Genode::log("Partition ", i, ": LBA ", log("Partition ", i, ": LBA ",
(unsigned int) r->_lba + offset, " (", (unsigned int) r._lba + offset, " (",
(unsigned int) r->_sectors, " blocks) type: ", (unsigned int) r._sectors, " blocks) type: ",
Genode::Hex(r->_type, Genode::Hex::OMIT_PREFIX)); Hex(r._type, Hex::OMIT_PREFIX));
if (!r->extended()) if (!r.extended())
_part_list[i] = new (&heap) _part_list[i] = new (heap)
Block::Partition(r->_lba + offset, r->_sectors); Block::Partition(r._lba + offset, r._sectors);
}); });
} }
/* Report the partitions */ /* check for AHDI partition table */
bool const ahdi_valid = !mbr_valid && Ahdi::valid(s);
if (ahdi_valid)
Ahdi::for_each_partition(s, [&] (unsigned i, Block::Partition info) {
if (i < MAX_PARTITIONS)
_part_list[i] = new (heap)
Block::Partition(info.lba, info.sectors); });
/* no partition table, use whole disc as partition 0 */
if (!mbr_valid && !ahdi_valid)
_part_list[0] = new (&heap)
Block::Partition(0, driver.blk_cnt() - 1);
/* report the partitions */
if (reporter.enabled()) { if (reporter.enabled()) {
enum { PROBE_BYTES = 4096, }; auto gen_partition_attr = [&] (Xml_generator &xml, unsigned i)
Genode::size_t const block_size = driver.blk_size(); {
Block::Partition const &part = *_part_list[i];
Genode::Reporter::Xml_generator xml(reporter, [&] () { size_t const block_size = driver.blk_size();
xml.attribute("number", i);
xml.attribute("start", part.lba);
xml.attribute("length", part.sectors);
xml.attribute("block_size", block_size);
/* probe for known file-system types */
enum { PROBE_BYTES = 4096, };
Sector fs(driver, part.lba, PROBE_BYTES / block_size);
Fs::Type const fs_type =
Fs::probe(fs.addr<uint8_t*>(), PROBE_BYTES);
if (fs_type.valid())
xml.attribute("file_system", fs_type);
};
Reporter::Xml_generator xml(reporter, [&] () {
xml.attribute("type", mbr_valid ? "mbr" :
ahdi_valid ? "ahdi" :
"disk");
if (mbr_valid) { if (mbr_valid) {
xml.attribute("type", "mbr"); _parse_mbr(mbr, [&] (int i, Partition_record const &r, unsigned) {
_parse_mbr(mbr, [&] (int i, Partition_record *r, unsigned offset) { /* nullptr if extended */
if (!_part_list[i])
Sector fs(driver, r->_lba + offset, PROBE_BYTES / block_size); return;
Fs::Type fs_type = Fs::probe(fs.addr<Genode::uint8_t*>(), PROBE_BYTES);
xml.node("partition", [&] { xml.node("partition", [&] {
xml.attribute("number", i); gen_partition_attr(xml, i);
xml.attribute("type", r->_type); xml.attribute("type", r._type); });
xml.attribute("start", r->_lba + offset);
xml.attribute("length", r->_sectors);
xml.attribute("block_size", driver.blk_size());
if (fs_type.valid()) {
xml.attribute("file_system", fs_type);
}
});
}); });
} else if (ahdi_valid) {
_for_each_valid_partition([&] (unsigned i) {
xml.node("partition", [&] {
gen_partition_attr(xml, i);
xml.attribute("type", "bgm"); }); });
} else { } else {
xml.attribute("type", "disk");
enum { PART_NUM = 0, };
Block::Partition const &disk = *_part_list[PART_NUM];
Sector fs(driver, disk.lba, PROBE_BYTES / block_size);
Fs::Type fs_type = Fs::probe(fs.addr<Genode::uint8_t*>(), PROBE_BYTES);
xml.node("partition", [&] { xml.node("partition", [&] {
xml.attribute("number", PART_NUM); gen_partition_attr(xml, 0); });
xml.attribute("start", disk.lba);
xml.attribute("length", disk.sectors + 1);
xml.attribute("block_size", driver.blk_size());
if (fs_type.valid()) {
xml.attribute("file_system", fs_type);
}
});
} }
}); });
} }
for (unsigned num = 0; num < MAX_PARTITIONS; num++) bool any_partition_valid = false;
if (_part_list[num]) _for_each_valid_partition([&] (unsigned) {
return true; any_partition_valid = true; });
return false;
return any_partition_valid;
} }
}; };

View File

@ -67,7 +67,7 @@ struct Block::Partition_table : Genode::Interface
~Sector() { _session.tx()->release_packet(_p); } ~Sector() { _session.tx()->release_packet(_p); }
template <typename T> T addr() { template <typename T> T addr() const {
return reinterpret_cast<T>(_session.tx()->packet_content(_p)); } return reinterpret_cast<T>(_session.tx()->packet_content(_p)); }
}; };