245 lines
5.6 KiB
C++
245 lines
5.6 KiB
C++
/*
|
|
* \brief Back end to other block interface
|
|
* \author Sebsastian Sumpf
|
|
* \date 2011-05-24
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2011-2013 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.
|
|
*/
|
|
#include <base/allocator_avl.h>
|
|
#include <base/semaphore.h>
|
|
#include <block_session/connection.h>
|
|
#include "part_blk.h"
|
|
|
|
using namespace Genode;
|
|
|
|
/**
|
|
* Used to block until a packet has been freed
|
|
*/
|
|
static Semaphore _alloc_sem(0);
|
|
|
|
namespace Partition {
|
|
|
|
size_t _blk_cnt;
|
|
size_t _blk_size;
|
|
|
|
Allocator_avl _block_alloc(env()->heap());
|
|
Block::Connection _blk(&_block_alloc, 4 * MAX_PACKET_SIZE);
|
|
|
|
Partition *_part_list[MAX_PARTITIONS]; /* contains pointers to valid partittions or 0 */
|
|
|
|
Partition *partition(int num) { return (num < MAX_PARTITIONS) ? _part_list[num] : 0; }
|
|
size_t blk_size() { return _blk_size; }
|
|
inline unsigned long max_packets() { return (MAX_PACKET_SIZE / _blk_size); }
|
|
void sync() { _blk.sync(); }
|
|
|
|
/**
|
|
* Partition table entry format
|
|
*/
|
|
struct Partition_record
|
|
{
|
|
enum { INVALID = 0, EXTENTED = 0x5 };
|
|
uint8_t _unused[4];
|
|
uint8_t _type; /* partition type */
|
|
uint8_t _unused2[3];
|
|
uint32_t _lba; /* logical block address */
|
|
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
|
|
{
|
|
uint8_t _unused[446];
|
|
Partition_record _records[4];
|
|
uint16_t _magic;
|
|
|
|
bool is_valid()
|
|
{
|
|
/* magic number of partition table */
|
|
enum { MAGIC = 0xaa55 };
|
|
return _magic == MAGIC;
|
|
}
|
|
} __attribute__((packed));
|
|
|
|
|
|
class Sector
|
|
{
|
|
private:
|
|
|
|
Block::Packet_descriptor _p;
|
|
|
|
protected:
|
|
|
|
static Lock _lock;
|
|
|
|
public:
|
|
|
|
Sector(unsigned long blk_nr, unsigned long count, bool write = false)
|
|
{
|
|
using namespace Block;
|
|
Lock::Guard guard(_lock);
|
|
Block::Packet_descriptor::Opcode op = write ? Block::Packet_descriptor::WRITE
|
|
: Block::Packet_descriptor::READ;
|
|
_p = Block::Packet_descriptor(_blk.dma_alloc_packet(_blk_size * count),
|
|
op, blk_nr, count);
|
|
}
|
|
|
|
void submit_request()
|
|
{
|
|
Lock::Guard guard(_lock);
|
|
_blk.tx()->submit_packet(_p);
|
|
_p = _blk.tx()->get_acked_packet();
|
|
|
|
/* unblock clients that possibly wait for packet stream allocations */
|
|
if (_alloc_sem.cnt() < 0)
|
|
_alloc_sem.up();
|
|
|
|
if (!_p.succeeded()) {
|
|
PERR("Could not access block %zu", _p.block_number());
|
|
throw Io_error();
|
|
}
|
|
}
|
|
|
|
~Sector()
|
|
{
|
|
Lock::Guard guard(_lock);
|
|
_blk.tx()->release_packet(_p);
|
|
}
|
|
|
|
template <typename T>
|
|
T addr() { return reinterpret_cast<T>(_blk.tx()->packet_content(_p)); }
|
|
};
|
|
|
|
Lock Sector::_lock;
|
|
|
|
|
|
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);
|
|
s.submit_request();
|
|
Mbr *ebr = s.addr<Mbr *>();
|
|
|
|
if (!(ebr->is_valid()))
|
|
throw Io_error();
|
|
|
|
/* 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 (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(env()->heap())Partition(0, _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(env()->heap()) Partition(r->_lba, r->_sectors);
|
|
}
|
|
}
|
|
|
|
|
|
void init()
|
|
{
|
|
Block::Session::Operations ops;
|
|
|
|
/* device info */
|
|
_blk.info(&_blk_cnt, &_blk_size, &ops);
|
|
|
|
/* read MBR */
|
|
Sector s(0, 1);
|
|
s.submit_request();
|
|
parse_mbr(s.addr<Mbr *>());
|
|
}
|
|
|
|
|
|
void _io(unsigned long lba, unsigned long count, uint8_t *buf, bool write)
|
|
{
|
|
unsigned long bytes;
|
|
|
|
while (count) {
|
|
|
|
unsigned long curr_count = min<unsigned long>(count, max_packets());
|
|
bytes = curr_count * _blk_size;
|
|
bool alloc = false;
|
|
while (!alloc) {
|
|
try {
|
|
Sector sec(lba, curr_count, write);
|
|
|
|
if (!write) {
|
|
sec.submit_request();
|
|
memcpy(buf, sec.addr<void *>(), bytes);
|
|
}
|
|
else {
|
|
memcpy(sec.addr<void *>(), buf, bytes);
|
|
sec.submit_request();
|
|
}
|
|
|
|
alloc = true;
|
|
} catch (Block::Session::Tx::Source::Packet_alloc_failed) {
|
|
/* block */
|
|
_alloc_sem.down();
|
|
}
|
|
}
|
|
|
|
lba += curr_count;
|
|
count -= curr_count;
|
|
buf += bytes;
|
|
}
|
|
|
|
/* zero out rest of page */
|
|
bytes = (count * _blk_size) % 4096;
|
|
if (bytes)
|
|
memset(buf, 0, bytes);
|
|
}
|
|
|
|
|
|
void Partition::io(unsigned long block_nr, unsigned long count, void *buf, bool write)
|
|
{
|
|
if (block_nr + count > _sectors)
|
|
throw Io_error();
|
|
|
|
_io(_lba + block_nr, count, (uint8_t *)buf, write);
|
|
}
|
|
}
|