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:
parent
23c2606ce0
commit
ceae637416
98
repos/os/src/server/part_block/ahdi.h
Normal file
98
repos/os/src/server/part_block/ahdi.h
Normal 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 §or)
|
||||||
|
{
|
||||||
|
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 §or, 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_ */
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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)); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user