mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 02:40:08 +00:00
ahci: switch to request stream API + structural changes
- Remove dated 'Block::Driver' front end and implement 'Block::Request_stream' front end - Remove all dynamic memory allocations but DMA memory allocations - Remove 'Platform_hba' and implement platform specific functions in 'spec/<platform>/*' - Ata and Atapi don't inherit from 'Port' any more, but are a member of 'Port' as a protocol implementation - Use platform driver for MMIO mappings (x86) - Exchange stateful initialization of Ata/Atapi with a sequential initialization using 'wait_for_any' and 'retry' patterns - Fix Atapi initialization by setting the byte count limit - Set FIS receive base only when Cmd::FRE is disabled and Cmd::FR is 0 - Put everything in namespaces ('Ahci', 'Ata', or 'Atapi') - Ata decides during read/write operations to use native-command queuing or normal DMA requests - Remove port claiming logic (is now done via 'Constructibles') fixes #3636
This commit is contained in:
parent
73f2c7043c
commit
8a7deae238
@ -62,9 +62,10 @@ append config {
|
||||
<start name="ahci_drv">
|
||||
<resource name="RAM" quantum="10M" />
|
||||
<provides><service name="Block" /></provides>
|
||||
<config>
|
||||
<config atapi="yes">
|
||||
<report ports="yes"/>
|
||||
<policy label_prefix="test-ahci" device="0" writeable="yes" />
|
||||
<policy label="test-ahci -> " device="0" writeable="yes" />
|
||||
<policy label="test-ahci-atapi -> " device="1" writeable="no" />
|
||||
</config>
|
||||
<route>
|
||||
<service name="Report"> <child name="ahci_report_rom"/> </service>
|
||||
@ -80,6 +81,15 @@ append config {
|
||||
<any-service> <parent/> <any-child /> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="test-ahci-atapi">
|
||||
<binary name="test-block-client" />
|
||||
<resource name="RAM" quantum="50M" />
|
||||
<config test_size="100M"></config>
|
||||
<route>
|
||||
<service name="Block"><child name="ahci_drv"/></service>
|
||||
<any-service> <parent/> <any-child /> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
</config> }
|
||||
|
||||
install_config $config
|
||||
@ -96,8 +106,8 @@ build_boot_image $boot_modules
|
||||
append qemu_args " -nographic -device ahci,id=ahci -boot d "
|
||||
append qemu_args " -drive id=disk,file=bin/ext2.raw,format=raw,if=none -device ide-hd,drive=disk,bus=ahci.0 "
|
||||
append qemu_args " -drive id=cd,file=[run_dir]/../ahci_block.iso,if=none,media=cdrom -device ide-cd,drive=cd,bus=ahci.1 "
|
||||
append qemu_args " -drive id=disk2,file=bin/ext2.raw,format=raw,if=none -device ide-hd,drive=disk2,bus=ahci.2 "
|
||||
|
||||
run_genode_until "Tests finished successfully!" 100
|
||||
run_genode_until "Tests finished successfully!" 100 [output_spawn_id]
|
||||
|
||||
exec rm -f bin/ext2.raw
|
||||
|
@ -1,278 +0,0 @@
|
||||
/**
|
||||
* \brief AHCI port controller handling
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-04-29
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 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.
|
||||
*/
|
||||
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include <ata_driver.h>
|
||||
#include <atapi_driver.h>
|
||||
|
||||
|
||||
struct Ahci
|
||||
{
|
||||
Genode::Env &env;
|
||||
Genode::Allocator &alloc;
|
||||
|
||||
/* read device signature */
|
||||
enum Signature {
|
||||
ATA_SIG = 0x101,
|
||||
ATAPI_SIG = 0xeb140101,
|
||||
ATAPI_SIG_QEMU = 0xeb140000, /* will be fixed in Qemu */
|
||||
};
|
||||
|
||||
struct Timer_delayer : Mmio::Delayer, Timer::Connection
|
||||
{
|
||||
Timer_delayer(Genode::Env &env)
|
||||
: Timer::Connection(env) { }
|
||||
|
||||
void usleep(uint64_t us) override { Timer::Connection::usleep(us); }
|
||||
} _delayer { env };
|
||||
|
||||
Ahci_root &root;
|
||||
Platform::Hba &platform_hba = Platform::init(env, _delayer);
|
||||
Hba hba { env, platform_hba, _delayer };
|
||||
|
||||
enum { MAX_PORTS = 32 };
|
||||
Port_driver *ports[MAX_PORTS];
|
||||
bool port_claimed[MAX_PORTS];
|
||||
|
||||
Signal_handler<Ahci> irq;
|
||||
unsigned ready_count = 0;
|
||||
bool enable_atapi;
|
||||
|
||||
Signal_context_capability device_identified;
|
||||
|
||||
Ahci(Genode::Env &env, Genode::Allocator &alloc,
|
||||
Ahci_root &root, bool support_atapi,
|
||||
Genode::Signal_context_capability device_identified)
|
||||
:
|
||||
env(env), alloc(alloc),
|
||||
root(root), irq(root.entrypoint(), *this, &Ahci::handle_irq),
|
||||
enable_atapi(support_atapi),
|
||||
device_identified(device_identified)
|
||||
{
|
||||
info();
|
||||
|
||||
/* register irq handler */
|
||||
platform_hba.sigh_irq(irq);
|
||||
|
||||
/* initialize HBA (IRQs, memory) */
|
||||
hba.init();
|
||||
|
||||
/* search for devices */
|
||||
scan_ports(env.rm(), env.ram());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward IRQs to ports
|
||||
*/
|
||||
void handle_irq()
|
||||
{
|
||||
unsigned port_list = hba.read<Hba::Is>();
|
||||
while (port_list) {
|
||||
unsigned port = log2(port_list);
|
||||
port_list &= ~(1U << port);
|
||||
|
||||
ports[port]->handle_irq();
|
||||
}
|
||||
|
||||
/* clear status register */
|
||||
hba.ack_irq();
|
||||
|
||||
/* ack at interrupt controller */
|
||||
platform_hba.ack_irq();
|
||||
}
|
||||
|
||||
/*
|
||||
* Least significant bit
|
||||
*/
|
||||
unsigned lsb(unsigned bits) const
|
||||
{
|
||||
for (unsigned i = 0; i < 32; i++)
|
||||
if (bits & (1u << i)) {
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void info()
|
||||
{
|
||||
using Genode::log;
|
||||
|
||||
log("version: "
|
||||
"major=", Genode::Hex(hba.read<Hba::Version::Major>()), " "
|
||||
"minor=", Genode::Hex(hba.read<Hba::Version::Minor>()));
|
||||
log("command slots: ", hba.command_slots());
|
||||
log("native command queuing: ", hba.ncq() ? "yes" : "no");
|
||||
log("64-bit support: ", hba.supports_64bit() ? "yes" : "no");
|
||||
}
|
||||
|
||||
void scan_ports(Genode::Region_map &rm, Genode::Ram_allocator &ram)
|
||||
{
|
||||
log("number of ports: ", hba.port_count(), " pi: ",
|
||||
Hex(hba.read<Hba::Pi>()));
|
||||
|
||||
unsigned available = hba.read<Hba::Pi>();
|
||||
for (unsigned i = 0; i < hba.port_count(); i++) {
|
||||
|
||||
/* check if port is implemented */
|
||||
if (!available) break;
|
||||
unsigned index = lsb(available);
|
||||
available ^= (1u << index);
|
||||
|
||||
bool enabled = false;
|
||||
|
||||
switch (Port_base(index, hba).read<Port_base::Sig>()) {
|
||||
case ATA_SIG:
|
||||
try {
|
||||
ports[index] = new (&alloc)
|
||||
Ata_driver(alloc, ram, root, ready_count, rm, hba,
|
||||
platform_hba, index, device_identified);
|
||||
enabled = true;
|
||||
} catch (...) { }
|
||||
|
||||
log("\t\t#", index, ":", enabled ? " ATA" : " off (ATA)");
|
||||
break;
|
||||
|
||||
case ATAPI_SIG:
|
||||
case ATAPI_SIG_QEMU:
|
||||
if (enable_atapi)
|
||||
try {
|
||||
ports[index] = new (&alloc)
|
||||
Atapi_driver(ram, root, ready_count, rm, hba,
|
||||
platform_hba, index);
|
||||
enabled = true;
|
||||
} catch (...) { }
|
||||
|
||||
log("\t\t#", index, ":", enabled ? " ATAPI" : " off (ATAPI)");
|
||||
break;
|
||||
|
||||
default:
|
||||
log("\t\t#", index, ": off (unknown device signature)");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Block::Driver *claim_port(unsigned port_num)
|
||||
{
|
||||
if (!avail(port_num))
|
||||
throw -1;
|
||||
|
||||
port_claimed[port_num] = true;
|
||||
return ports[port_num];
|
||||
}
|
||||
|
||||
void free_port(unsigned port_num)
|
||||
{
|
||||
port_claimed[port_num] = false;
|
||||
}
|
||||
|
||||
bool avail(unsigned port_num)
|
||||
{
|
||||
return port_num < MAX_PORTS && ports[port_num] && !port_claimed[port_num] &&
|
||||
ports[port_num]->ready();
|
||||
}
|
||||
|
||||
Port_driver * port(unsigned num)
|
||||
{
|
||||
return num < MAX_PORTS ? ports[num] : nullptr;
|
||||
}
|
||||
|
||||
long device_number(const char *model_num, const char *serial_num)
|
||||
{
|
||||
for (long port_num = 0; port_num < MAX_PORTS; port_num++) {
|
||||
Ata_driver* drv = dynamic_cast<Ata_driver *>(ports[port_num]);
|
||||
if (!drv)
|
||||
continue;
|
||||
|
||||
if (*drv->model == model_num && *drv->serial == serial_num)
|
||||
return port_num;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Ahci(Ahci const &);
|
||||
Ahci &operator = (Ahci const &);
|
||||
};
|
||||
|
||||
|
||||
static Ahci *sata_ahci(Ahci *ahci = 0)
|
||||
{
|
||||
static Ahci *a = ahci;
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
void Ahci_driver::init(Genode::Env &env, Genode::Allocator &alloc,
|
||||
Ahci_root &root, bool support_atapi,
|
||||
Genode::Signal_context_capability device_identified)
|
||||
{
|
||||
static Ahci ahci(env, alloc, root, support_atapi, device_identified);
|
||||
sata_ahci(&ahci);
|
||||
}
|
||||
|
||||
|
||||
Block::Driver *Ahci_driver::claim_port(long device_num)
|
||||
{
|
||||
return sata_ahci()->claim_port(device_num);
|
||||
}
|
||||
|
||||
|
||||
void Ahci_driver::free_port(long device_num)
|
||||
{
|
||||
sata_ahci()->free_port(device_num);
|
||||
}
|
||||
|
||||
|
||||
bool Ahci_driver::avail(long device_num)
|
||||
{
|
||||
return sata_ahci()->avail(device_num);
|
||||
}
|
||||
|
||||
|
||||
long Ahci_driver::device_number(char const *model_num, char const *serial_num)
|
||||
{
|
||||
return sata_ahci()->device_number(model_num, serial_num);
|
||||
}
|
||||
|
||||
|
||||
void Ahci_driver::report_ports(Genode::Reporter &reporter)
|
||||
{
|
||||
Genode::Reporter::Xml_generator xml(reporter, [&] () {
|
||||
for (unsigned i = 0; i < Ahci::MAX_PORTS; ++i) {
|
||||
Port_driver *port = sata_ahci()->port(i);
|
||||
if (!port || !port->ready()) continue;
|
||||
|
||||
Ata_driver *ata = dynamic_cast<Ata_driver *>(port);
|
||||
|
||||
xml.node("port", [&] () {
|
||||
xml.attribute("num", i);
|
||||
xml.attribute("type", ata ? "ATA" : "ATAPI");
|
||||
if (ata) {
|
||||
xml.attribute("block_count", ata->block_count());
|
||||
xml.attribute("block_size", ata->block_size());
|
||||
xml.attribute("model", ata->model->cstring());
|
||||
xml.attribute("serial", ata->serial->cstring());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,427 +0,0 @@
|
||||
/*
|
||||
* \brief AHCI-port driver for ATA devices
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-04-29
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 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 _ATA_DRIVER_H_
|
||||
#define _ATA_DRIVER_H_
|
||||
|
||||
#include <base/log.h>
|
||||
#include "ahci.h"
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/**
|
||||
* Return data of 'identify_device' ATA command
|
||||
*/
|
||||
struct Identity : Genode::Mmio
|
||||
{
|
||||
Identity(Genode::addr_t base) : Mmio(base) { }
|
||||
|
||||
struct Serial_number : Register_array<0x14, 8, 20, 8> { };
|
||||
struct Model_number : Register_array<0x36, 8, 40, 8> { };
|
||||
|
||||
struct Queue_depth : Register<0x96, 16>
|
||||
{
|
||||
struct Max_depth : Bitfield<0, 5> { };
|
||||
};
|
||||
|
||||
struct Sata_caps : Register<0x98, 16>
|
||||
{
|
||||
struct Ncq_support : Bitfield<8, 1> { };
|
||||
};
|
||||
|
||||
struct Sector_count : Register<0xc8, 64> { };
|
||||
|
||||
struct Logical_block : Register<0xd4, 16>
|
||||
{
|
||||
struct Per_physical : Bitfield<0, 3> { }; /* 2^X logical per physical */
|
||||
struct Longer_512 : Bitfield<12, 1> { };
|
||||
struct Multiple : Bitfield<13, 1> { }; /* multiple logical blocks per physical */
|
||||
};
|
||||
|
||||
struct Logical_words : Register<0xea, 32> { }; /* words (16 bit) per logical block */
|
||||
|
||||
struct Alignment : Register<0x1a2, 16>
|
||||
{
|
||||
struct Logical_offset : Bitfield<0, 14> { }; /* offset first logical block in physical */
|
||||
};
|
||||
|
||||
void info()
|
||||
{
|
||||
using Genode::log;
|
||||
|
||||
log(" queue depth: ", read<Queue_depth::Max_depth>() + 1, " "
|
||||
"ncq: ", read<Sata_caps::Ncq_support>());
|
||||
log(" numer of sectors: ", read<Sector_count>());
|
||||
log(" multiple logical blocks per physical: ",
|
||||
read<Logical_block::Multiple>() ? "yes" : "no");
|
||||
log(" logical blocks per physical: ",
|
||||
1U << read<Logical_block::Per_physical>());
|
||||
log(" logical block size is above 512 byte: ",
|
||||
read<Logical_block::Longer_512>() ? "yes" : "no");
|
||||
log(" words (16bit) per logical block: ",
|
||||
read<Logical_words>());
|
||||
log(" offset of first logical block within physical: ",
|
||||
read<Alignment::Logical_offset>());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 16-bit word big endian device ASCII characters
|
||||
*/
|
||||
template <typename DEVICE_STRING>
|
||||
struct String
|
||||
{
|
||||
char buf[DEVICE_STRING::ITEMS + 1];
|
||||
|
||||
String(Identity & info)
|
||||
{
|
||||
long j = 0;
|
||||
for (unsigned long i = 0; i < DEVICE_STRING::ITEMS; i++) {
|
||||
/* read and swap even and uneven characters */
|
||||
char c = (char)info.read<DEVICE_STRING>(i ^ 1);
|
||||
if (Genode::is_whitespace(c) && j == 0)
|
||||
continue;
|
||||
buf[j++] = c;
|
||||
}
|
||||
|
||||
buf[j] = 0;
|
||||
|
||||
/* remove trailing white spaces */
|
||||
while ((j > 0) && (buf[--j] == ' '))
|
||||
buf[j] = 0;
|
||||
}
|
||||
|
||||
bool operator == (char const *other) const
|
||||
{
|
||||
return strcmp(buf, other) == 0;
|
||||
}
|
||||
|
||||
void print(Genode::Output &out) const { Genode::print(out, (char const *)buf); }
|
||||
char const *cstring() { return buf; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Commands to distinguish between ncq and non-ncq operation
|
||||
*/
|
||||
struct Io_command : Interface
|
||||
{
|
||||
virtual void command(Port &por,
|
||||
Command_table &table,
|
||||
bool read,
|
||||
Block::sector_t block_number,
|
||||
size_t count,
|
||||
unsigned slot) = 0;
|
||||
|
||||
virtual void handle_irq(Port &port, Port::Is::access_t status) = 0;
|
||||
};
|
||||
|
||||
struct Ncq_command : Io_command
|
||||
{
|
||||
void command(Port &port,
|
||||
Command_table &table,
|
||||
bool read,
|
||||
Block::sector_t block_number,
|
||||
size_t count,
|
||||
unsigned slot) override
|
||||
{
|
||||
table.fis.fpdma(read, block_number, count, slot);
|
||||
/* ensure that 'Cmd::St' is 1 before writing 'Sact' */
|
||||
port.start();
|
||||
/* set pending */
|
||||
port.write<Port::Sact>(1U << slot);
|
||||
}
|
||||
|
||||
void handle_irq(Port &port, Port::Is::access_t status) override
|
||||
{
|
||||
/*
|
||||
* Check for completions of other requests immediately
|
||||
*/
|
||||
while (Port::Is::Sdbs::get(status = port.read<Port::Is>()))
|
||||
port.ack_irq();
|
||||
}
|
||||
};
|
||||
|
||||
struct Dma_ext_command : Io_command
|
||||
{
|
||||
void command(Port &,
|
||||
Command_table &table,
|
||||
bool read,
|
||||
Block::sector_t block_number,
|
||||
size_t count,
|
||||
unsigned /* slot */) override
|
||||
{
|
||||
table.fis.dma_ext(read, block_number, count);
|
||||
}
|
||||
|
||||
void handle_irq(Port &port, Port::Is::access_t status) override
|
||||
{
|
||||
if (Port::Is::Dma_ext_irq::get(status))
|
||||
port.ack_irq();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Drivers using ncq- and non-ncq commands
|
||||
*/
|
||||
struct Ata_driver : Port_driver
|
||||
{
|
||||
Genode::Allocator &alloc;
|
||||
|
||||
typedef ::String<Identity::Serial_number> Serial_string;
|
||||
typedef ::String<Identity::Model_number> Model_string;
|
||||
|
||||
Genode::Constructible<Identity> identity { };
|
||||
Genode::Constructible<Serial_string> serial { };
|
||||
Genode::Constructible<Model_string> model { };
|
||||
|
||||
Io_command *io_cmd = nullptr;
|
||||
Block::Packet_descriptor pending[32];
|
||||
|
||||
Signal_context_capability device_identified;
|
||||
|
||||
Ata_driver(Genode::Allocator &alloc,
|
||||
Genode::Ram_allocator &ram,
|
||||
Ahci_root &root,
|
||||
unsigned &sem,
|
||||
Genode::Region_map &rm,
|
||||
Hba &hba,
|
||||
Platform::Hba &platform_hba,
|
||||
unsigned number,
|
||||
Genode::Signal_context_capability device_identified)
|
||||
: Port_driver(ram, root, sem, rm, hba, platform_hba, number),
|
||||
alloc(alloc), device_identified(device_identified)
|
||||
{
|
||||
Port::init();
|
||||
identify_device();
|
||||
}
|
||||
|
||||
~Ata_driver()
|
||||
{
|
||||
if (io_cmd)
|
||||
destroy(&alloc, io_cmd);
|
||||
}
|
||||
|
||||
unsigned find_free_cmd_slot()
|
||||
{
|
||||
for (unsigned slot = 0; slot < cmd_slots; slot++)
|
||||
if (!pending[slot].size())
|
||||
return slot;
|
||||
|
||||
throw Block::Driver::Request_congestion();
|
||||
}
|
||||
|
||||
void ack_packets()
|
||||
{
|
||||
unsigned slots = Port::read<Ci>() | Port::read<Sact>();
|
||||
|
||||
for (unsigned slot = 0; slot < cmd_slots; slot++) {
|
||||
if ((slots & (1U << slot)) || !pending[slot].size())
|
||||
continue;
|
||||
|
||||
Block::Packet_descriptor p = pending[slot];
|
||||
pending[slot] = Block::Packet_descriptor();
|
||||
ack_packet(p, true);
|
||||
}
|
||||
}
|
||||
|
||||
void overlap_check(Block::sector_t block_number,
|
||||
size_t count)
|
||||
{
|
||||
Block::sector_t end = block_number + count - 1;
|
||||
|
||||
for (unsigned slot = 0; slot < cmd_slots; slot++) {
|
||||
if (!pending[slot].size())
|
||||
continue;
|
||||
|
||||
Block::sector_t pending_start = pending[slot].block_number();
|
||||
Block::sector_t pending_end = pending_start + pending[slot].block_count() - 1;
|
||||
/* check if a pending packet overlaps */
|
||||
if ((block_number >= pending_start && block_number <= pending_end) ||
|
||||
(end >= pending_start && end <= pending_end) ||
|
||||
(pending_start >= block_number && pending_start <= end) ||
|
||||
(pending_end >= block_number && pending_end <= end)) {
|
||||
|
||||
Genode::warning("overlap: "
|
||||
"pending ", pending[slot].block_number(),
|
||||
" + ", pending[slot].block_count(), ", "
|
||||
"request: ", block_number, " + ", count);
|
||||
throw Block::Driver::Request_congestion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void io(bool read,
|
||||
Block::sector_t block_number,
|
||||
size_t count,
|
||||
addr_t phys,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
sanity_check(block_number, count);
|
||||
overlap_check(block_number, count);
|
||||
|
||||
unsigned slot = find_free_cmd_slot();
|
||||
pending[slot] = packet;
|
||||
|
||||
/* setup fis */
|
||||
Command_table table(command_table_addr(slot), phys, count * block_size());
|
||||
|
||||
/* set ATA command */
|
||||
io_cmd->command(*this, table, read, block_number, count, slot);
|
||||
|
||||
/* set or clear write flag in command header */
|
||||
Command_header header(command_header_addr(slot));
|
||||
header.write<Command_header::Bits::W>(read ? 0 : 1);
|
||||
header.clear_byte_count();
|
||||
|
||||
execute(slot);
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
** Port_driver **
|
||||
*****************/
|
||||
|
||||
void handle_irq() override
|
||||
{
|
||||
Is::access_t status = Port::read<Is>();
|
||||
|
||||
switch (state) {
|
||||
|
||||
case IDENTIFY:
|
||||
|
||||
if (Port::Is::Dss::get(status)
|
||||
|| Port::Is::Pss::get(status)
|
||||
|| Port::Is::Dhrs::get(status)) {
|
||||
identity.construct(device_info);
|
||||
serial.construct(*identity);
|
||||
model.construct(*identity);
|
||||
|
||||
if (verbose) {
|
||||
Genode::log(" model number: ", Genode::Cstring(model->buf));
|
||||
Genode::log(" serial number: ", Genode::Cstring(serial->buf));
|
||||
identity->info();
|
||||
}
|
||||
|
||||
check_device();
|
||||
if (ncq_support())
|
||||
io_cmd = new (&alloc) Ncq_command();
|
||||
else
|
||||
io_cmd = new (&alloc) Dma_ext_command();
|
||||
|
||||
ack_irq();
|
||||
|
||||
Genode::Signal_transmitter(device_identified).submit();
|
||||
}
|
||||
break;
|
||||
|
||||
case READY:
|
||||
|
||||
io_cmd->handle_irq(*this, status);
|
||||
ack_packets();
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
stop();
|
||||
}
|
||||
|
||||
bool ncq_support()
|
||||
{
|
||||
return identity->read<Identity::Sata_caps::Ncq_support>() && hba.ncq();
|
||||
}
|
||||
|
||||
void check_device()
|
||||
{
|
||||
cmd_slots = min((int)cmd_slots,
|
||||
identity->read<Identity::Queue_depth::Max_depth >() + 1);
|
||||
|
||||
/* no native command queueing */
|
||||
if (!ncq_support())
|
||||
cmd_slots = 1;
|
||||
|
||||
state = READY;
|
||||
state_change();
|
||||
}
|
||||
|
||||
void identify_device()
|
||||
{
|
||||
state = IDENTIFY;
|
||||
addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
|
||||
|
||||
Command_table table(command_table_addr(0), phys, 0x1000);
|
||||
table.fis.identify_device();
|
||||
execute(0);
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
** Block::Driver interface **
|
||||
*****************************/
|
||||
|
||||
bool dma_enabled() override { return true; };
|
||||
|
||||
Block::Session::Info info() const override
|
||||
{
|
||||
return { .block_size = block_size(),
|
||||
.block_count = block_count(),
|
||||
.align_log2 = log2(block_size()),
|
||||
.writeable = true };
|
||||
}
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
addr_t phys,
|
||||
Block::Packet_descriptor &packet) override
|
||||
{
|
||||
io(true, block_number, block_count, phys, packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
addr_t phys,
|
||||
Block::Packet_descriptor &packet) override
|
||||
{
|
||||
io(false, block_number, block_count, phys, packet);
|
||||
}
|
||||
|
||||
Genode::size_t block_size() const override
|
||||
{
|
||||
Genode::size_t size = 512;
|
||||
|
||||
if (identity->read<Identity::Logical_block::Longer_512>())
|
||||
size = identity->read<Identity::Logical_words>() / 2;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
Block::sector_t block_count() const override
|
||||
{
|
||||
return identity->read<Identity::Sector_count>();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Ata_driver(Ata_driver const &);
|
||||
Ata_driver &operator = (Ata_driver const &);
|
||||
};
|
||||
|
||||
|
||||
#endif /* _ATA_DRIVER_H_ */
|
344
repos/os/src/drivers/ahci/ata_protocol.h
Normal file
344
repos/os/src/drivers/ahci/ata_protocol.h
Normal file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* \brief ATA protocol driver
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-04-29
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2020 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 _AHCI__ATA_PROTOCOL_H_
|
||||
#define _AHCI__ATA_PROTOCOL_H_
|
||||
|
||||
#include <base/log.h>
|
||||
#include "ahci.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace Ata {
|
||||
struct Identity;
|
||||
template <typename DEVICE_STRING> struct String;
|
||||
class Protocol;
|
||||
using namespace Genode;
|
||||
using namespace Ahci;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return data of 'identify_device' ATA command
|
||||
*/
|
||||
struct Ata::Identity : Mmio
|
||||
{
|
||||
Identity(addr_t base) : Mmio(base) { }
|
||||
|
||||
struct Serial_number : Register_array<0x14, 8, 20, 8> { };
|
||||
struct Model_number : Register_array<0x36, 8, 40, 8> { };
|
||||
|
||||
struct Queue_depth : Register<0x96, 16>
|
||||
{
|
||||
struct Max_depth : Bitfield<0, 5> { };
|
||||
};
|
||||
|
||||
struct Sata_caps : Register<0x98, 16>
|
||||
{
|
||||
struct Ncq_support : Bitfield<8, 1> { };
|
||||
};
|
||||
|
||||
struct Sector_count : Register<0xc8, 64> { };
|
||||
|
||||
struct Logical_block : Register<0xd4, 16>
|
||||
{
|
||||
struct Per_physical : Bitfield<0, 3> { }; /* 2^X logical per physical */
|
||||
struct Longer_512 : Bitfield<12, 1> { };
|
||||
struct Multiple : Bitfield<13, 1> { }; /* multiple logical blocks per physical */
|
||||
};
|
||||
|
||||
struct Logical_words : Register<0xea, 32> { }; /* words (16 bit) per logical block */
|
||||
|
||||
struct Alignment : Register<0x1a2, 16>
|
||||
{
|
||||
struct Logical_offset : Bitfield<0, 14> { }; /* offset first logical block in physical */
|
||||
};
|
||||
|
||||
void info()
|
||||
{
|
||||
log(" queue depth: ", read<Queue_depth::Max_depth>() + 1, " "
|
||||
"ncq: ", read<Sata_caps::Ncq_support>());
|
||||
log(" numer of sectors: ", read<Sector_count>());
|
||||
log(" multiple logical blocks per physical: ",
|
||||
read<Logical_block::Multiple>() ? "yes" : "no");
|
||||
log(" logical blocks per physical: ",
|
||||
1U << read<Logical_block::Per_physical>());
|
||||
log(" logical block size is above 512 byte: ",
|
||||
read<Logical_block::Longer_512>() ? "yes" : "no");
|
||||
log(" words (16bit) per logical block: ",
|
||||
read<Logical_words>());
|
||||
log(" offset of first logical block within physical: ",
|
||||
read<Alignment::Logical_offset>());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 16-bit word big endian device ASCII characters
|
||||
*/
|
||||
template <typename DEVICE_STRING>
|
||||
struct Ata::String
|
||||
{
|
||||
char buf[DEVICE_STRING::ITEMS + 1];
|
||||
|
||||
String(Identity & info)
|
||||
{
|
||||
long j = 0;
|
||||
for (unsigned long i = 0; i < DEVICE_STRING::ITEMS; i++) {
|
||||
/* read and swap even and uneven characters */
|
||||
char c = (char)info.read<DEVICE_STRING>(i ^ 1);
|
||||
if (is_whitespace(c) && j == 0)
|
||||
continue;
|
||||
buf[j++] = c;
|
||||
}
|
||||
|
||||
buf[j] = 0;
|
||||
|
||||
/* remove trailing white spaces */
|
||||
while ((j > 0) && (buf[--j] == ' '))
|
||||
buf[j] = 0;
|
||||
}
|
||||
|
||||
bool operator == (char const *other) const
|
||||
{
|
||||
return strcmp(buf, other) == 0;
|
||||
}
|
||||
|
||||
void print(Output &out) const { print(out, (char const *)buf); }
|
||||
char const *cstring() { return buf; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Protocol driver using ncq- and non-ncq commands
|
||||
*/
|
||||
class Ata::Protocol : public Ahci::Protocol, Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
struct Request : Block::Request
|
||||
{
|
||||
bool valid() const { return operation.valid(); }
|
||||
void invalidate() { operation.type = Block::Operation::Type::INVALID; }
|
||||
|
||||
Request & operator=(const Block::Request &request)
|
||||
{
|
||||
operation = request.operation;
|
||||
success = request.success;
|
||||
offset = request.offset;
|
||||
tag = request.tag;
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
Util::Slots<Request, 32> _slots { };
|
||||
unsigned _slot_states = 0;
|
||||
|
||||
typedef String<Identity::Serial_number> Serial_string;
|
||||
typedef String<Identity::Model_number> Model_string;
|
||||
|
||||
Constructible<Identity> _identity { };
|
||||
bool _writeable { false };
|
||||
|
||||
public:
|
||||
|
||||
Constructible<Serial_string> serial { };
|
||||
Constructible<Model_string> model { };
|
||||
|
||||
private:
|
||||
|
||||
bool _overlap_check(Block::Request const &request)
|
||||
{
|
||||
block_number_t block_number = request.operation.block_number;
|
||||
block_number_t end = block_number + request.operation.count - 1;
|
||||
|
||||
auto overlap_check = [&] (Request const &req) {
|
||||
block_number_t pending_start = req.operation.block_number;
|
||||
block_number_t pending_end = pending_start + req.operation.count - 1;
|
||||
|
||||
/* check if a pending packet overlaps */
|
||||
if ((block_number >= pending_start && block_number <= pending_end) ||
|
||||
(end >= pending_start && end <= pending_end) ||
|
||||
(pending_start >= block_number && pending_start <= end) ||
|
||||
(pending_end >= block_number && pending_end <= end)) {
|
||||
|
||||
warning("overlap: "
|
||||
"pending ", pending_start,
|
||||
" + ", req.operation.count, ", "
|
||||
"request: ", block_number, " + ", request.operation.count);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
return _slots.for_each(overlap_check);
|
||||
}
|
||||
|
||||
bool _ncq_support(Port &port)
|
||||
{
|
||||
return _identity->read<Identity::Sata_caps::Ncq_support>() && port.hba.ncq();
|
||||
}
|
||||
|
||||
size_t _block_size() const
|
||||
{
|
||||
size_t size = 512;
|
||||
|
||||
if (_identity->read<Identity::Logical_block::Longer_512>())
|
||||
size = _identity->read<Identity::Logical_words>() / 2;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
Block::sector_t _block_count() const
|
||||
{
|
||||
return _identity->read<Identity::Sector_count>();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/******************************
|
||||
** Ahci::Protocol interface **
|
||||
******************************/
|
||||
|
||||
unsigned init(Port &port) override
|
||||
{
|
||||
/* identify device */
|
||||
addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
|
||||
|
||||
Command_table table(port.command_table_addr(0), phys, 0x1000);
|
||||
table.fis.identify_device();
|
||||
port.execute(0);
|
||||
|
||||
port.wait_for_any(port.hba.delayer(), Port::Is::Dss::Equal(1),
|
||||
Port::Is::Pss::Equal(1),
|
||||
Port::Is::Dhrs::Equal(1));
|
||||
|
||||
_identity.construct(port.device_info);
|
||||
serial.construct(*_identity);
|
||||
model.construct(*_identity);
|
||||
|
||||
if (verbose) {
|
||||
log(" model number: ", Cstring(model->buf));
|
||||
log(" serial number: ", Cstring(serial->buf));
|
||||
_identity->info();
|
||||
}
|
||||
|
||||
/* read number of command slots of ATA device */
|
||||
unsigned cmd_slots = _identity->read<Identity::Queue_depth::Max_depth >() + 1;
|
||||
|
||||
/* no native command queueing */
|
||||
if (!_ncq_support(port))
|
||||
cmd_slots = 1;
|
||||
|
||||
_slots.limit((size_t)cmd_slots);
|
||||
port.ack_irq();
|
||||
|
||||
return cmd_slots;
|
||||
}
|
||||
|
||||
void handle_irq(Port &port) override
|
||||
{
|
||||
/* ncg */
|
||||
if (_ncq_support(port))
|
||||
while (Port::Is::Sdbs::get(port.read<Port::Is>()))
|
||||
port.ack_irq();
|
||||
/* normal dma */
|
||||
else if (Port::Is::Dma_ext_irq::get(port.read<Port::Is>()))
|
||||
port.ack_irq();
|
||||
|
||||
_slot_states = port.read<Port::Ci>() | port.read<Port::Sact>();
|
||||
port.stop();
|
||||
}
|
||||
|
||||
Block::Session::Info info() const override
|
||||
{
|
||||
return { .block_size = _block_size(),
|
||||
.block_count = _block_count(),
|
||||
.align_log2 = log2(2ul),
|
||||
.writeable = _writeable };
|
||||
}
|
||||
|
||||
void writeable(bool rw) override { _writeable = rw; }
|
||||
|
||||
Response submit(Port &port, Block::Request const request) override
|
||||
{
|
||||
if (port.sanity_check(request) == false || port.dma_base == 0)
|
||||
return Response::REJECTED;
|
||||
|
||||
if (_writeable == false && request.operation.type == Block::Operation::Type::WRITE)
|
||||
return Response::REJECTED;
|
||||
|
||||
if (_overlap_check(request))
|
||||
return Response::RETRY;
|
||||
|
||||
Request *r = _slots.get();
|
||||
|
||||
if (r == nullptr)
|
||||
return Response::RETRY;
|
||||
|
||||
*r = request;
|
||||
|
||||
Block::Operation op = request.operation;
|
||||
size_t slot = _slots.index(*r);
|
||||
_slot_states |= 1u << slot;
|
||||
|
||||
/* setup fis */
|
||||
Command_table table(port.command_table_addr(slot),
|
||||
port.dma_base + request.offset, /* physical address */
|
||||
op.count * _block_size());
|
||||
|
||||
/* setup ATA command */
|
||||
bool read = op.type == Block::Operation::Type::READ;
|
||||
|
||||
if (_ncq_support(port)) {
|
||||
table.fis.fpdma(read, op.block_number, op.count, slot);
|
||||
/* ensure that 'Cmd::St' is 1 before writing 'Sact' */
|
||||
port.start();
|
||||
/* set pending */
|
||||
port.write<Port::Sact>(1U << slot);
|
||||
} else {
|
||||
table.fis.dma_ext(read, op.block_number, op.count);
|
||||
}
|
||||
|
||||
/* set or clear write flag in command header */
|
||||
Command_header header(port.command_header_addr(slot));
|
||||
header.write<Command_header::Bits::W>(read ? 0 : 1);
|
||||
header.clear_byte_count();
|
||||
|
||||
port.execute(slot);
|
||||
|
||||
return Response::ACCEPTED;
|
||||
}
|
||||
|
||||
Block::Request completed(Port & /* port */) override
|
||||
{
|
||||
Block::Request r { };
|
||||
|
||||
_slots.for_each([&](Request &request)
|
||||
{
|
||||
size_t index = _slots.index(request);
|
||||
/* request still pending */
|
||||
if (_slot_states & (1u << index))
|
||||
return false;
|
||||
|
||||
r = request;
|
||||
request.invalidate();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _AHCI__ATA_PROTOCOL_H_ */
|
@ -1,218 +1,195 @@
|
||||
/*
|
||||
* \brief AHCI-port driver for ATAPI devices
|
||||
* \brief ATAPI protocol driver
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-04-29
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2015-2020 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 _ATAPI_DRIVER_H_
|
||||
#define _ATAPI_DRIVER_H_
|
||||
#ifndef _AHCI__ATAPI_PROTOCOL_H_
|
||||
#define _AHCI__ATAPI_PROTOCOL_H_
|
||||
|
||||
#include "ahci.h"
|
||||
#include <util/endian.h>
|
||||
|
||||
namespace Atapi {
|
||||
class Protocol;
|
||||
using namespace Ahci;
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
struct Atapi_driver : Port_driver
|
||||
class Atapi::Protocol : public Ahci::Protocol, Noncopyable
|
||||
{
|
||||
unsigned sense_tries = 0;
|
||||
Block::Packet_descriptor pending { };
|
||||
private:
|
||||
|
||||
Atapi_driver(Genode::Ram_allocator &ram,
|
||||
Ahci_root &root,
|
||||
unsigned &sem,
|
||||
Genode::Region_map &rm,
|
||||
Hba &hba,
|
||||
Platform::Hba &platform_hba,
|
||||
unsigned number)
|
||||
: Port_driver(ram, root, sem, rm, hba, platform_hba, number)
|
||||
{
|
||||
Port::init();
|
||||
Port::write<Cmd::Atapi>(1);
|
||||
read_sense();
|
||||
}
|
||||
Block::Request _pending { };
|
||||
block_number_t _block_count { 0 };
|
||||
size_t _block_size { 2048 };
|
||||
|
||||
void atapi_command()
|
||||
{
|
||||
Command_header header(command_header_addr(0));
|
||||
header.atapi_command();
|
||||
header.clear_byte_count();
|
||||
execute(0);
|
||||
}
|
||||
|
||||
void test_unit_ready()
|
||||
{
|
||||
state = TEST_READY;
|
||||
|
||||
Command_table table(command_table_addr(0), 0, 0);
|
||||
table.fis.atapi();
|
||||
table.atapi_cmd.test_unit_ready();
|
||||
|
||||
atapi_command();
|
||||
}
|
||||
|
||||
void read_sense()
|
||||
{
|
||||
state = STATUS;
|
||||
|
||||
if (sense_tries++ >= 3) {
|
||||
Genode::error("could not power up device");
|
||||
state_change();
|
||||
return;
|
||||
void _atapi_command(Port &port)
|
||||
{
|
||||
Command_header header(port.command_header_addr(0));
|
||||
header.atapi_command();
|
||||
header.clear_byte_count();
|
||||
port.execute(0);
|
||||
}
|
||||
|
||||
addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
|
||||
void _read_sense(Port &port)
|
||||
{
|
||||
addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
|
||||
|
||||
Command_table table(command_table_addr(0), phys, 0x1000);
|
||||
table.fis.atapi();
|
||||
table.atapi_cmd.read_sense();
|
||||
Command_table table(port.command_table_addr(0), phys, 0x1000);
|
||||
table.fis.atapi();
|
||||
table.atapi_cmd.read_sense();
|
||||
|
||||
atapi_command();
|
||||
}
|
||||
|
||||
void read_capacity()
|
||||
{
|
||||
state = IDENTIFY;
|
||||
addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
|
||||
|
||||
Command_table table(command_table_addr(0), phys, 0x1000);
|
||||
table.fis.atapi();
|
||||
table.atapi_cmd.read_capacity();
|
||||
|
||||
atapi_command();
|
||||
}
|
||||
|
||||
void ack_packets()
|
||||
{
|
||||
unsigned slots = Port::read<Ci>();
|
||||
|
||||
if (slots & 1 || !pending.size())
|
||||
return;
|
||||
|
||||
Block::Packet_descriptor p = pending;
|
||||
pending = Block::Packet_descriptor();
|
||||
ack_packet(p, true);
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
** Port_driver **
|
||||
*****************/
|
||||
|
||||
void handle_irq() override
|
||||
{
|
||||
Is::access_t status = Port::read<Is>();
|
||||
|
||||
if (verbose) {
|
||||
Genode::log("irq: "
|
||||
"is: ", Genode::Hex(status), " "
|
||||
"ci: ", Genode::Hex(Port::read<Ci>()), " "
|
||||
"state: ", (int)state);
|
||||
Device_fis f(fis_base);
|
||||
Genode::log("d2h: "
|
||||
"status: ", f.read<Device_fis::Status>(), " "
|
||||
"error: ", Genode::Hex(f.read<Device_fis::Error>()));
|
||||
_atapi_command(port);
|
||||
}
|
||||
|
||||
ack_irq();
|
||||
void _test_unit_ready(Port &port)
|
||||
{
|
||||
Command_table table(port.command_table_addr(0), 0, 0);
|
||||
table.fis.atapi();
|
||||
table.atapi_cmd.test_unit_ready();
|
||||
|
||||
if (state == TEST_READY && Port::Is::Dhrs::get(status)) {
|
||||
Device_fis f(fis_base);
|
||||
|
||||
/* check if devic is ready */
|
||||
if (f.read<Device_fis::Status::Device_ready>() && !f.read<Device_fis::Error>())
|
||||
read_capacity();
|
||||
else
|
||||
read_sense();
|
||||
_atapi_command(port);
|
||||
}
|
||||
|
||||
if (state == READY && Port::Is::Dhrs::get(status)) {
|
||||
ack_packets();
|
||||
void _read_capacity(Port &port)
|
||||
{
|
||||
addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
|
||||
|
||||
Command_table table(port.command_table_addr(0), phys, 0x1000);
|
||||
table.fis.atapi();
|
||||
table.fis.byte_count(~0);
|
||||
|
||||
table.atapi_cmd.read_capacity();
|
||||
|
||||
_atapi_command(port);
|
||||
}
|
||||
|
||||
if (Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) {
|
||||
switch (state) {
|
||||
void _start_unit(Port &port)
|
||||
{
|
||||
Command_table table(port.command_table_addr(0), 0, 0);
|
||||
table.fis.atapi();
|
||||
table.atapi_cmd.start_unit();
|
||||
|
||||
case STATUS:
|
||||
test_unit_ready();
|
||||
break;
|
||||
|
||||
case IDENTIFY:
|
||||
state = READY;
|
||||
state_change();
|
||||
break;
|
||||
|
||||
case READY:
|
||||
ack_packets();
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_atapi_command(port);
|
||||
}
|
||||
}
|
||||
|
||||
Block::Session::Info info() const override
|
||||
{
|
||||
return { .block_size = block_size(),
|
||||
.block_count = block_count(),
|
||||
.align_log2 = 11,
|
||||
.writeable = false };
|
||||
}
|
||||
public:
|
||||
|
||||
/******************************
|
||||
** Ahci::Protocol interface **
|
||||
******************************/
|
||||
|
||||
/*****************************
|
||||
** Block::Driver interface **
|
||||
*****************************/
|
||||
unsigned init(Port &port) override
|
||||
{
|
||||
port.write<Port::Cmd::Atapi>(1);
|
||||
|
||||
bool dma_enabled() override { return true; };
|
||||
retry<Port::Polling_timeout>(
|
||||
[&] {
|
||||
|
||||
Genode::size_t block_size() const override
|
||||
{
|
||||
return host_to_big_endian(((unsigned *)device_info)[1]);
|
||||
}
|
||||
_start_unit(port);
|
||||
port.wait_for_any(port.hba.delayer(),
|
||||
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
|
||||
Port::Is::Dhrs::Equal(1));
|
||||
port.ack_irq();
|
||||
|
||||
Block::sector_t block_count() const override
|
||||
{
|
||||
return host_to_big_endian(((unsigned *)device_info)[0]) + 1;
|
||||
}
|
||||
/* read sense */
|
||||
_read_sense(port);
|
||||
port.wait_for_any(port.hba.delayer(),
|
||||
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
|
||||
Port::Is::Dhrs::Equal(1));
|
||||
port.ack_irq();
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
size_t count,
|
||||
addr_t phys,
|
||||
Block::Packet_descriptor &packet) override
|
||||
{
|
||||
if (pending.size())
|
||||
throw Block::Driver::Request_congestion();
|
||||
/* test unit ready */
|
||||
_test_unit_ready(port);
|
||||
port.wait_for(port.hba.delayer(), Port::Is::Dhrs::Equal(1));
|
||||
port.ack_irq();
|
||||
|
||||
sanity_check(block_number, count);
|
||||
Device_fis f(port.fis_base);
|
||||
/* check if devic is ready */
|
||||
if (!f.read<Device_fis::Status::Device_ready>() || f.read<Device_fis::Error>())
|
||||
throw Port::Polling_timeout();
|
||||
|
||||
pending = packet;
|
||||
_read_capacity(port);
|
||||
port.wait_for_any(port.hba.delayer(),
|
||||
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
|
||||
Port::Is::Dhrs::Equal(1));
|
||||
port.ack_irq();
|
||||
|
||||
if (verbose)
|
||||
Genode::log("add packet read ", block_number, " count ", count, " -> 0");
|
||||
_block_count = host_to_big_endian(((unsigned *)port.device_info)[0]) + 1;
|
||||
_block_size = host_to_big_endian(((unsigned *)port.device_info)[1]);
|
||||
},
|
||||
[&] {}, 3);
|
||||
|
||||
/* setup fis */
|
||||
Command_table table(command_table_addr(0), phys, count * block_size());
|
||||
table.fis.atapi();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* setup atapi command */
|
||||
table.atapi_cmd.read10(block_number, count);
|
||||
Block::Session::Info info() const override
|
||||
{
|
||||
return { .block_size = _block_size,
|
||||
.block_count = _block_count,
|
||||
.align_log2 = 1,
|
||||
.writeable = false };
|
||||
}
|
||||
|
||||
/* set and clear write flag in command header */
|
||||
Command_header header(command_header_addr(0));
|
||||
header.write<Command_header::Bits::W>(0);
|
||||
header.clear_byte_count();
|
||||
void handle_irq(Port &port) override
|
||||
{
|
||||
port.ack_irq();
|
||||
}
|
||||
|
||||
/* set pending */
|
||||
execute(0);
|
||||
}
|
||||
void writeable(bool) override { }
|
||||
|
||||
Response submit(Port &port, Block::Request const request) override
|
||||
{
|
||||
if (port.sanity_check(request) == false || port.dma_base == 0 ||
|
||||
request.operation.type != Block::Operation::Type::READ)
|
||||
return Response::REJECTED;
|
||||
|
||||
if (_pending.operation.valid())
|
||||
return Response::RETRY;
|
||||
|
||||
Block::Operation op = request.operation;
|
||||
_pending = request;
|
||||
_pending.success = false;
|
||||
|
||||
/* setup fis */
|
||||
Command_table table(port.command_table_addr(0),
|
||||
port.dma_base + request.offset,
|
||||
op.count * _block_size);
|
||||
table.fis.atapi();
|
||||
|
||||
/* setup atapi command */
|
||||
table.atapi_cmd.read10(op.block_number, op.count);
|
||||
table.fis.byte_count(~0);
|
||||
|
||||
/* set and clear write flag in command header */
|
||||
Command_header header(port.command_header_addr(0));
|
||||
header.write<Command_header::Bits::W>(0);
|
||||
header.clear_byte_count();
|
||||
|
||||
/* set pending */
|
||||
port.execute(0);
|
||||
|
||||
return Response::ACCEPTED;
|
||||
}
|
||||
|
||||
Block::Request completed(Port &port) override
|
||||
{
|
||||
if (!_pending.operation.valid() || port.read<Port::Ci>())
|
||||
return Block::Request();
|
||||
|
||||
Block::Request request = _pending;
|
||||
_pending.operation.type = Block::Operation::Type::INVALID;
|
||||
|
||||
return request;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _ATAPI_DRIVER_H_ */
|
||||
#endif /* _AHCI__ATAPI_PROTOCOL_H_ */
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2016-2020 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.
|
||||
@ -14,195 +14,394 @@
|
||||
/* Genode includes */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/log.h>
|
||||
#include <block/component.h>
|
||||
#include <block/request_stream.h>
|
||||
#include <os/session_policy.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <util/xml_node.h>
|
||||
#include <os/reporter.h>
|
||||
|
||||
/* local includes */
|
||||
#include <ahci.h>
|
||||
#include <ata_protocol.h>
|
||||
#include <atapi_protocol.h>
|
||||
|
||||
|
||||
namespace Block {
|
||||
class Factory;
|
||||
class Root_multiple_clients;
|
||||
class Main;
|
||||
namespace Ahci {
|
||||
struct Dispatch;
|
||||
class Driver;
|
||||
struct Main;
|
||||
struct Block_session_handler;
|
||||
struct Block_session_component;
|
||||
}
|
||||
|
||||
|
||||
struct Block::Factory : Driver_factory
|
||||
struct Ahci::Dispatch : Interface
|
||||
{
|
||||
long device_num;
|
||||
|
||||
Block::Driver *create() override
|
||||
{
|
||||
return Ahci_driver::claim_port(device_num);
|
||||
}
|
||||
|
||||
void destroy(Block::Driver *) override
|
||||
{
|
||||
Ahci_driver::free_port(device_num);
|
||||
}
|
||||
|
||||
Factory(long device_num) : device_num(device_num) { }
|
||||
virtual void session(unsigned index) = 0;
|
||||
};
|
||||
|
||||
|
||||
class Session_component : public Block::Session_component
|
||||
class Ahci::Driver : Noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
Session_component(Block::Driver_factory &driver_factory,
|
||||
Genode::Entrypoint &ep,
|
||||
Genode::Region_map &rm,
|
||||
Genode::size_t buf_size,
|
||||
bool writeable)
|
||||
: Block::Session_component(driver_factory, ep, rm, buf_size, writeable) { }
|
||||
enum { MAX_PORTS = 32 };
|
||||
|
||||
Block::Driver_factory &factory() { return _driver_factory; }
|
||||
};
|
||||
|
||||
|
||||
class Block::Root_multiple_clients : public Root_component< ::Session_component>,
|
||||
public Ahci_root
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Env &_env;
|
||||
Genode::Allocator &_alloc;
|
||||
Genode::Xml_node _config;
|
||||
Env &_env;
|
||||
Dispatch &_dispatch;
|
||||
|
||||
protected:
|
||||
|
||||
::Session_component *_create_session(const char *args) override
|
||||
struct Timer_delayer : Mmio::Delayer, Timer::Connection
|
||||
{
|
||||
Session_label const label = label_from_args(args);
|
||||
Session_policy const policy(label, _config);
|
||||
Timer_delayer(Env &env)
|
||||
: Timer::Connection(env) { }
|
||||
|
||||
size_t ram_quota =
|
||||
Arg_string::find_arg(args, "ram_quota").ulong_value(0);
|
||||
size_t tx_buf_size =
|
||||
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
|
||||
void usleep(uint64_t us) override { Timer::Connection::usleep(us); }
|
||||
} _delayer { _env };
|
||||
|
||||
if (!tx_buf_size)
|
||||
throw Service_denied();
|
||||
Hba _hba { _env, _delayer };
|
||||
|
||||
size_t session_size = sizeof(::Session_component)
|
||||
+ sizeof(Factory) + tx_buf_size;
|
||||
Constructible<Ata::Protocol> _ata[MAX_PORTS];
|
||||
Constructible<Atapi::Protocol> _atapi[MAX_PORTS];
|
||||
Constructible<Port> _ports[MAX_PORTS];
|
||||
|
||||
if (max((size_t)4096, session_size) > ram_quota) {
|
||||
error("insufficient 'ram_quota' from '", label, "',"
|
||||
" got ", ram_quota, ", need ", session_size);
|
||||
throw Insufficient_ram_quota();
|
||||
}
|
||||
Signal_handler<Driver> _irq { _env.ep(), *this, &Driver::handle_irq };
|
||||
bool _enable_atapi;
|
||||
|
||||
/* try read device port number attribute */
|
||||
long num = policy.attribute_value("device", -1L);
|
||||
|
||||
/* try read device model and serial number attributes */
|
||||
auto const model = policy.attribute_value("model", String<64>());
|
||||
auto const serial = policy.attribute_value("serial", String<64>());
|
||||
|
||||
/* sessions are not writeable by default */
|
||||
bool writeable = policy.attribute_value("writeable", false);
|
||||
|
||||
/* prefer model/serial routing */
|
||||
if ((model != "") && (serial != ""))
|
||||
num = Ahci_driver::device_number(model.string(), serial.string());
|
||||
|
||||
if (num < 0) {
|
||||
error("rejecting session request, no matching policy for '", label, "'",
|
||||
model == "" ? ""
|
||||
: " (model=", model, " serial=", serial, ")");
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
if (!Ahci_driver::avail(num)) {
|
||||
error("Device ", num, " not available");
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
if (writeable)
|
||||
writeable = Arg_string::find_arg(args, "writeable").bool_value(true);
|
||||
|
||||
Block::Factory *factory = new (&_alloc) Block::Factory(num);
|
||||
::Session_component *session = new (&_alloc)
|
||||
::Session_component(*factory, _env.ep(), _env.rm(), tx_buf_size, writeable);
|
||||
log(
|
||||
writeable ? "writeable " : "read-only ",
|
||||
"session opened at device ", num, " for '", label, "'");
|
||||
return session;
|
||||
void _info()
|
||||
{
|
||||
log("version: "
|
||||
"major=", Hex(_hba.read<Hba::Version::Major>()), " "
|
||||
"minor=", Hex(_hba.read<Hba::Version::Minor>()));
|
||||
log("command slots: ", _hba.command_slots());
|
||||
log("native command queuing: ", _hba.ncq() ? "yes" : "no");
|
||||
log("64-bit support: ", _hba.supports_64bit() ? "yes" : "no");
|
||||
}
|
||||
|
||||
void _destroy_session(::Session_component *session) override
|
||||
void _scan_ports(Region_map &rm)
|
||||
{
|
||||
Driver_factory &factory = session->factory();
|
||||
Genode::destroy(&_alloc, session);
|
||||
Genode::destroy(&_alloc, &factory);
|
||||
log("number of ports: ", _hba.port_count(), " pi: ",
|
||||
Hex(_hba.read<Hba::Pi>()));
|
||||
|
||||
for (unsigned index = 0; index < MAX_PORTS; index++) {
|
||||
|
||||
Port_base port(index, _hba);
|
||||
|
||||
if (port.implemented() == false)
|
||||
continue;
|
||||
|
||||
bool enabled = false;
|
||||
if (port.ata()) {
|
||||
try {
|
||||
_ata[index].construct();
|
||||
_ports[index].construct(*_ata[index], rm, _hba, index);
|
||||
enabled = true;
|
||||
} catch (...) { }
|
||||
|
||||
log("\t\t#", index, ":", enabled ? " ATA" : " off (ATA)");
|
||||
} else if (port.atapi() && _enable_atapi) {
|
||||
try {
|
||||
_atapi[index].construct();
|
||||
_ports[index].construct(*_atapi[index], rm, _hba, index);
|
||||
enabled = true;
|
||||
} catch (...) { }
|
||||
|
||||
log("\t\t#", index, ":", enabled ? " ATAPI" : " off (ATAPI)");
|
||||
} else {
|
||||
log("\t\t#", index, ":", port.atapi() ? " off (ATAPI)"
|
||||
: " off (unknown device signature)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Root_multiple_clients(Genode::Env &env, Genode::Allocator &alloc,
|
||||
Genode::Xml_node config)
|
||||
:
|
||||
Root_component(&env.ep().rpc_ep(), &alloc),
|
||||
_env(env), _alloc(alloc), _config(config)
|
||||
{ }
|
||||
|
||||
Genode::Entrypoint &entrypoint() override { return _env.ep(); }
|
||||
|
||||
void announce() override
|
||||
Driver(Env &env, Dispatch &dispatch, bool support_atapi)
|
||||
: _env(env), _dispatch(dispatch), _enable_atapi(support_atapi)
|
||||
{
|
||||
_env.parent().announce(_env.ep().manage(*this));
|
||||
_info();
|
||||
|
||||
/* register irq handler */
|
||||
_hba.sigh_irq(_irq);
|
||||
|
||||
/* initialize HBA (IRQs, memory) */
|
||||
_hba.init();
|
||||
|
||||
/* search for devices */
|
||||
_scan_ports(env.rm());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward IRQs to ports/block sessions
|
||||
*/
|
||||
void handle_irq()
|
||||
{
|
||||
unsigned port_list = _hba.read<Hba::Is>();
|
||||
while (port_list) {
|
||||
unsigned port = log2(port_list);
|
||||
port_list &= ~(1U << port);
|
||||
|
||||
/* ack irq */
|
||||
if (_ports[port].constructed())
|
||||
_ports[port]->handle_irq();
|
||||
|
||||
/* handle (pending) requests */
|
||||
_dispatch.session(port);
|
||||
}
|
||||
|
||||
/* clear status register */
|
||||
_hba.ack_irq();
|
||||
}
|
||||
Port &port(long device, char const *model_num, char const *serial_num)
|
||||
{
|
||||
/* check for model/device */
|
||||
if (model_num && serial_num) {
|
||||
for (long index = 0; index < MAX_PORTS; index++) {
|
||||
if (!_ata[index].constructed()) continue;
|
||||
|
||||
Ata::Protocol &protocol = *_ata[index];
|
||||
if (*protocol.model == model_num && *protocol.serial == serial_num)
|
||||
return *_ports[index];
|
||||
}
|
||||
}
|
||||
|
||||
/* check for device number */
|
||||
if (device >= 0 && device < MAX_PORTS && _ports[device].constructed())
|
||||
return *_ports[device];
|
||||
|
||||
throw -1;
|
||||
}
|
||||
|
||||
template <typename FN> void for_each_port(FN const &fn)
|
||||
{
|
||||
for (unsigned index = 0; index < MAX_PORTS; index++) {
|
||||
if (!_ports[index].constructed()) continue;
|
||||
fn(*_ports[index], index, !_ata[index].constructed());
|
||||
}
|
||||
}
|
||||
|
||||
void report_ports(Reporter &reporter)
|
||||
{
|
||||
auto report = [&](Port const &port, unsigned index, bool atapi) {
|
||||
|
||||
Block::Session::Info info = port.info();
|
||||
Reporter::Xml_generator xml(reporter, [&] () {
|
||||
|
||||
xml.node("port", [&] () {
|
||||
xml.attribute("num", index);
|
||||
xml.attribute("type", atapi ? "ATAPI" : "ATA");
|
||||
xml.attribute("block_count", info.block_count);
|
||||
xml.attribute("block_size", info.block_size);
|
||||
if (!atapi) {
|
||||
xml.attribute("model", _ata[index]->model->cstring());
|
||||
xml.attribute("serial", _ata[index]->serial->cstring());
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
for_each_port(report);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Block::Main
|
||||
struct Ahci::Block_session_handler : Interface
|
||||
{
|
||||
Genode::Env &env;
|
||||
Genode::Heap heap { env.ram(), env.rm() };
|
||||
Env &env;
|
||||
Port &port;
|
||||
Ram_dataspace_capability ds;
|
||||
|
||||
Genode::Attached_rom_dataspace config { env, "config" };
|
||||
Signal_handler<Block_session_handler> request_handler
|
||||
{ env.ep(), *this, &Block_session_handler::handle};
|
||||
|
||||
Genode::Constructible<Genode::Reporter> reporter { };
|
||||
Block_session_handler(Env &env, Port &port, size_t buffer_size)
|
||||
: env(env), port(port), ds(port.alloc_buffer(buffer_size))
|
||||
{ }
|
||||
|
||||
Block::Root_multiple_clients root;
|
||||
|
||||
Signal_handler<Main> device_identified {
|
||||
env.ep(), *this, &Main::handle_device_identified };
|
||||
|
||||
Main(Genode::Env &env)
|
||||
: env(env), root(env, heap, config.xml())
|
||||
~Block_session_handler()
|
||||
{
|
||||
Genode::log("--- Starting AHCI driver ---");
|
||||
port.free_buffer(ds);
|
||||
}
|
||||
|
||||
virtual void handle_requests()= 0;
|
||||
|
||||
void handle()
|
||||
{
|
||||
handle_requests();
|
||||
}
|
||||
};
|
||||
|
||||
struct Ahci::Block_session_component : Rpc_object<Block::Session>,
|
||||
Block_session_handler,
|
||||
Block::Request_stream
|
||||
{
|
||||
Block_session_component(Env &env, Port &port, size_t buffer_size)
|
||||
:
|
||||
Block_session_handler(env, port, buffer_size),
|
||||
Request_stream(env.rm(), ds, env.ep(), request_handler, port.info())
|
||||
{
|
||||
env.ep().manage(*this);
|
||||
}
|
||||
|
||||
~Block_session_component()
|
||||
{
|
||||
env.ep().dissolve(*this);
|
||||
}
|
||||
|
||||
Info info() const override { return Request_stream::info(); }
|
||||
|
||||
Capability<Tx> tx_cap() override { return Request_stream::tx_cap(); }
|
||||
|
||||
void handle_requests() override
|
||||
{
|
||||
while (true) {
|
||||
|
||||
bool progress = false;
|
||||
|
||||
/*
|
||||
* Acknowledge any pending packets before sending new request to the
|
||||
* controller.
|
||||
*/
|
||||
try_acknowledge([&](Ack &ack) {
|
||||
port.for_one_completed_request([&] (Block::Request request) {
|
||||
progress = true;
|
||||
ack.submit(request);
|
||||
});
|
||||
});
|
||||
|
||||
with_requests([&] (Block::Request request) {
|
||||
|
||||
Response response = Response::RETRY;
|
||||
|
||||
/* only READ/WRITE requests, others are noops for now */
|
||||
if (Block::Operation::has_payload(request.operation.type) == false) {
|
||||
request.success = true;
|
||||
progress = true;
|
||||
return Response::REJECTED;
|
||||
}
|
||||
|
||||
if ((response = port.submit(request)) != Response::RETRY)
|
||||
progress = true;
|
||||
|
||||
return response;
|
||||
});
|
||||
|
||||
if (progress == false) break;
|
||||
}
|
||||
|
||||
/* poke */
|
||||
wakeup_client_if_needed();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Ahci::Main : Rpc_object<Typed_root<Block::Session>>,
|
||||
Dispatch
|
||||
{
|
||||
Env &env;
|
||||
|
||||
Attached_rom_dataspace config { env, "config" };
|
||||
|
||||
Constructible<Ahci::Driver> driver { };
|
||||
Constructible<Reporter> reporter { };
|
||||
Constructible<Block_session_component> block_session[Driver::MAX_PORTS];
|
||||
|
||||
Main(Env &env)
|
||||
: env(env)
|
||||
{
|
||||
log("--- Starting AHCI driver ---");
|
||||
bool support_atapi = config.xml().attribute_value("atapi", false);
|
||||
try {
|
||||
Ahci_driver::init(env, heap, root, support_atapi, device_identified);
|
||||
} catch (Ahci_driver::Missing_controller) {
|
||||
Genode::error("no AHCI controller found");
|
||||
driver.construct(env, *this, support_atapi);
|
||||
report_ports();
|
||||
} catch (Ahci::Missing_controller) {
|
||||
error("no AHCI controller found");
|
||||
env.parent().exit(~0);
|
||||
} catch (Genode::Service_denied) {
|
||||
Genode::error("hardware access denied");
|
||||
} catch (Service_denied) {
|
||||
error("hardware access denied");
|
||||
env.parent().exit(~0);
|
||||
}
|
||||
|
||||
env.parent().announce(env.ep().manage(*this));
|
||||
}
|
||||
|
||||
void session(unsigned index) override
|
||||
{
|
||||
if (index > Driver::MAX_PORTS || !block_session[index].constructed()) return;
|
||||
block_session[index]->handle_requests();
|
||||
}
|
||||
|
||||
Session_capability session(Root::Session_args const &args,
|
||||
Affinity const &) override
|
||||
{
|
||||
Session_label const label = label_from_args(args.string());
|
||||
Session_policy const policy(label, config.xml());
|
||||
|
||||
Ram_quota const ram_quota = ram_quota_from_args(args.string());
|
||||
size_t const tx_buf_size =
|
||||
Arg_string::find_arg(args.string(), "tx_buf_size").ulong_value(0);
|
||||
|
||||
if (!tx_buf_size)
|
||||
throw Service_denied();
|
||||
|
||||
if (tx_buf_size > ram_quota.value) {
|
||||
error("insufficient 'ram_quota' from '", label, "',"
|
||||
" got ", ram_quota, ", need ", tx_buf_size);
|
||||
throw Insufficient_ram_quota();
|
||||
}
|
||||
|
||||
/* try read device port number attribute */
|
||||
long device = policy.attribute_value("device", -1L);
|
||||
|
||||
/* try read device model and serial number attributes */
|
||||
auto const model = policy.attribute_value("model", String<64>());
|
||||
auto const serial = policy.attribute_value("serial", String<64>());
|
||||
bool const writeable = policy.attribute_value("writeable", false);
|
||||
|
||||
try {
|
||||
Port &port = driver->port(device, model.string(), serial.string());
|
||||
|
||||
if (block_session[port.index].constructed()) {
|
||||
error("Device with number=", port.index, " is already in use");
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
port.writeable(writeable);
|
||||
block_session[port.index].construct(env, port, tx_buf_size);
|
||||
return block_session[port.index]->cap();
|
||||
} catch (...) {
|
||||
error("rejecting session request, no matching policy for '", label, "'",
|
||||
" (model=", model, " serial=", serial, " device index=", device, ")");
|
||||
}
|
||||
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
void upgrade(Session_capability, Root::Upgrade_args const&) override { }
|
||||
|
||||
void close(Session_capability cap) override
|
||||
{
|
||||
for (int index = 0; index < Driver::MAX_PORTS; index++) {
|
||||
if (!block_session[index].constructed() || !(cap == block_session[index]->cap()))
|
||||
continue;
|
||||
|
||||
block_session[index].destruct();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_device_identified()
|
||||
void report_ports()
|
||||
{
|
||||
try {
|
||||
Xml_node report = config.xml().sub_node("report");
|
||||
if (report.attribute_value("ports", false)) {
|
||||
reporter.construct(env, "ports");
|
||||
reporter->enabled(true);
|
||||
Ahci_driver::report_ports(*reporter);
|
||||
driver->report_ports(*reporter);
|
||||
}
|
||||
} catch (Genode::Xml_node::Nonexistent_sub_node) { }
|
||||
} catch (Xml_node::Nonexistent_sub_node) { }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Block::Main server(env); }
|
||||
void Component::construct(Genode::Env &env) { static Ahci::Main server(env); }
|
||||
|
@ -1,156 +1,87 @@
|
||||
/*
|
||||
* \brief Driver for PCI-bus platforms
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-04-29
|
||||
* \date 2020-01-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#include <irq_session/connection.h>
|
||||
#include <platform_session/connection.h>
|
||||
#include <platform_device/client.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
#include <ahci.h>
|
||||
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
struct X86_hba : Platform::Hba
|
||||
Ahci::Data::Data(Env &env)
|
||||
: env(env)
|
||||
{
|
||||
enum Pci_config {
|
||||
CLASS_MASS_STORAGE = 0x10000u,
|
||||
SUBCLASS_AHCI = 0x600u,
|
||||
CLASS_MASK = 0xffff00u,
|
||||
AHCI_DEVICE = CLASS_MASS_STORAGE | SUBCLASS_AHCI,
|
||||
AHCI_BASE_ID = 0x5, /* resource id of ahci base addr <bar 5> */
|
||||
PCI_CMD = 0x4,
|
||||
};
|
||||
pci_device_cap = pci.with_upgrade(
|
||||
[&] () { return pci.next_device(pci_device_cap, AHCI_DEVICE,
|
||||
CLASS_MASK); });
|
||||
|
||||
Genode::Env &env;
|
||||
|
||||
Platform::Connection pci { env };
|
||||
Platform::Device_capability pci_device_cap { };
|
||||
Constructible<Platform::Device_client> pci_device { };
|
||||
Constructible<Irq_session_client> irq { };
|
||||
addr_t res_base { 0 };
|
||||
size_t res_size { 0 };
|
||||
|
||||
X86_hba(Genode::Env &env) : env(env)
|
||||
{
|
||||
pci_device_cap = pci.with_upgrade(
|
||||
[&] () { return pci.next_device(pci_device_cap, AHCI_DEVICE,
|
||||
CLASS_MASK); });
|
||||
|
||||
if (!pci_device_cap.valid()) {
|
||||
throw Ahci_driver::Missing_controller();
|
||||
}
|
||||
|
||||
/* construct pci client */
|
||||
pci_device.construct(pci_device_cap);
|
||||
Genode::log("AHCI found ("
|
||||
"vendor: ", Genode::Hex(pci_device->vendor_id()), " "
|
||||
"device: ", Genode::Hex(pci_device->device_id()), " "
|
||||
"class: ", Genode::Hex(pci_device->class_code()), ")");
|
||||
|
||||
/* read base address of controller */
|
||||
Platform::Device::Resource resource = pci_device->resource(AHCI_BASE_ID);
|
||||
res_base = resource.base();
|
||||
res_size = resource.size();
|
||||
|
||||
uint16_t cmd = pci_device->config_read(PCI_CMD, Platform::Device::ACCESS_16BIT);
|
||||
cmd |= 0x2; /* respond to memory space accesses */
|
||||
cmd |= 0x4; /* enable bus master */
|
||||
_config_write(PCI_CMD, cmd, Platform::Device::ACCESS_16BIT);
|
||||
|
||||
irq.construct(pci_device->irq(0));
|
||||
if (!pci_device_cap.valid()) {
|
||||
throw Missing_controller();
|
||||
}
|
||||
|
||||
void disable_msi()
|
||||
{
|
||||
enum { PM_CAP_OFF = 0x34, MSI_CAP = 0x5, MSI_ENABLED = 0x1 };
|
||||
uint8_t cap = pci_device->config_read(PM_CAP_OFF, Platform::Device::ACCESS_8BIT);
|
||||
/* construct pci client */
|
||||
pci_device.construct(pci_device_cap);
|
||||
log("AHCI found ("
|
||||
"vendor: ", Hex(pci_device->vendor_id()), " "
|
||||
"device: ", Hex(pci_device->device_id()), " "
|
||||
"class: ", Hex(pci_device->class_code()), ")");
|
||||
|
||||
/* iterate through cap pointers */
|
||||
for (uint16_t val = 0; cap; cap = val >> 8) {
|
||||
val = pci_device->config_read(cap, Platform::Device::ACCESS_16BIT);
|
||||
/* map base address of controller */
|
||||
Io_mem_session_capability iomem_cap = pci_device->io_mem(pci_device->phys_bar_to_virt(AHCI_BASE_ID));
|
||||
iomem.construct(env.rm(), Io_mem_session_client(iomem_cap).dataspace());
|
||||
|
||||
if ((val & 0xff) != MSI_CAP)
|
||||
continue;
|
||||
uint16_t cmd = pci_device->config_read(PCI_CMD, ::Platform::Device::ACCESS_16BIT);
|
||||
cmd |= 0x2; /* respond to memory space accesses */
|
||||
cmd |= 0x4; /* enable bus master */
|
||||
_config_write(PCI_CMD, cmd, ::Platform::Device::ACCESS_16BIT);
|
||||
|
||||
uint16_t msi = pci_device->config_read(cap + 2, Platform::Device::ACCESS_16BIT);
|
||||
|
||||
if (msi & MSI_ENABLED) {
|
||||
_config_write(cap + 2, msi ^ MSI_CAP,
|
||||
Platform::Device::ACCESS_8BIT);
|
||||
Genode::log("disabled MSI ", msi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _config_write(uint8_t op, uint16_t cmd,
|
||||
Platform::Device::Access_size width)
|
||||
{
|
||||
Genode::size_t donate = 4096;
|
||||
Genode::retry<Platform::Out_of_ram>(
|
||||
[&] () {
|
||||
Genode::retry<Platform::Out_of_caps>(
|
||||
[&] () { pci_device->config_write(op, cmd, width); },
|
||||
[&] () { pci.upgrade_caps(2); });
|
||||
},
|
||||
[&] () {
|
||||
pci.upgrade_ram(donate);
|
||||
donate *= 2;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
** Hba interface **
|
||||
*******************/
|
||||
|
||||
Genode::addr_t base() const override { return res_base; }
|
||||
Genode::size_t size() const override { return res_size; }
|
||||
|
||||
void sigh_irq(Signal_context_capability sigh) override
|
||||
{
|
||||
irq->sigh(sigh);
|
||||
ack_irq();
|
||||
}
|
||||
|
||||
void ack_irq() override { irq->ack_irq(); }
|
||||
|
||||
Ram_dataspace_capability
|
||||
alloc_dma_buffer(Genode::size_t size) override
|
||||
{
|
||||
size_t donate = size;
|
||||
|
||||
return retry<Genode::Out_of_ram>(
|
||||
[&] () {
|
||||
return retry<Genode::Out_of_caps>(
|
||||
[&] () { return pci.alloc_dma_buffer(size); },
|
||||
[&] () { pci.upgrade_caps(2); });
|
||||
},
|
||||
[&] () {
|
||||
pci.upgrade_ram(donate);
|
||||
donate = donate * 2 > size ? 4096 : donate * 2;
|
||||
});
|
||||
}
|
||||
|
||||
void free_dma_buffer(Genode::Ram_dataspace_capability ds) override
|
||||
{
|
||||
pci.free_dma_buffer(ds);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Platform::Hba &Platform::init(Genode::Env &env, Mmio::Delayer &)
|
||||
{
|
||||
static X86_hba h(env);
|
||||
return h;
|
||||
irq.construct(pci_device->irq(0));
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
** Platform interface **
|
||||
************************/
|
||||
|
||||
Genode::addr_t Ahci::Platform::_mmio_base() const
|
||||
{
|
||||
return addr_t(_data.iomem->local_addr<addr_t>());
|
||||
}
|
||||
|
||||
|
||||
void Ahci::Platform::sigh_irq(Signal_context_capability sigh)
|
||||
{
|
||||
_data.irq->sigh(sigh);
|
||||
ack_irq();
|
||||
}
|
||||
|
||||
|
||||
void Ahci::Platform::ack_irq() { _data.irq->ack_irq(); }
|
||||
|
||||
|
||||
Genode::Ram_dataspace_capability Ahci::Platform::alloc_dma_buffer(size_t size)
|
||||
{
|
||||
size_t donate = size;
|
||||
|
||||
return retry<Genode::Out_of_ram>(
|
||||
[&] () {
|
||||
return retry<Genode::Out_of_caps>(
|
||||
[&] () { return _data.pci.alloc_dma_buffer(size); },
|
||||
[&] () { _data.pci.upgrade_caps(2); });
|
||||
},
|
||||
[&] () {
|
||||
_data.pci.upgrade_ram(donate);
|
||||
donate = donate * 2 > size ? 4096 : donate * 2;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Ahci::Platform::free_dma_buffer(Genode::Ram_dataspace_capability ds)
|
||||
{
|
||||
_data.pci.free_dma_buffer(ds);
|
||||
}
|
||||
|
65
repos/os/src/drivers/ahci/spec/x86/platform.h
Normal file
65
repos/os/src/drivers/ahci/spec/x86/platform.h
Normal file
@ -0,0 +1,65 @@
|
||||
#ifndef _AHCI__SPEC__X86__PLATFORM_H_
|
||||
#define _AHCI__SPEC__X86__PLATFORM_H_
|
||||
/*
|
||||
* \brief Driver for PCI-bus platforms
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2020-01-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#include <irq_session/connection.h>
|
||||
#include <platform_session/connection.h>
|
||||
#include <platform_device/client.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
namespace Ahci {
|
||||
struct Data;
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
struct Ahci::Data
|
||||
{
|
||||
enum Pci_config {
|
||||
CLASS_MASS_STORAGE = 0x10000u,
|
||||
SUBCLASS_AHCI = 0x600u,
|
||||
CLASS_MASK = 0xffff00u,
|
||||
AHCI_DEVICE = CLASS_MASS_STORAGE | SUBCLASS_AHCI,
|
||||
AHCI_BASE_ID = 0x5, /* resource id of ahci base addr <bar 5> */
|
||||
PCI_CMD = 0x4,
|
||||
};
|
||||
|
||||
Genode::Env &env;
|
||||
|
||||
Platform::Connection pci { env };
|
||||
Platform::Device_capability pci_device_cap { };
|
||||
Constructible<Platform::Device_client> pci_device { };
|
||||
Constructible<Irq_session_client> irq { };
|
||||
Constructible<Attached_dataspace> iomem { };
|
||||
|
||||
Data(Env &env);
|
||||
|
||||
void _config_write(uint8_t op, uint16_t cmd,
|
||||
Platform::Device::Access_size width)
|
||||
{
|
||||
size_t donate = 4096;
|
||||
retry<Platform::Out_of_ram>(
|
||||
[&] () {
|
||||
retry<Platform::Out_of_caps>(
|
||||
[&] () { pci_device->config_write(op, cmd, width); },
|
||||
[&] () { pci.upgrade_caps(2); });
|
||||
},
|
||||
[&] () {
|
||||
pci.upgrade_ram(donate);
|
||||
donate *= 2;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* _AHCI__SPEC__X86__PLATFORM_H_ */
|
@ -1,5 +1,7 @@
|
||||
TARGET = ahci_drv
|
||||
REQUIRES = x86
|
||||
|
||||
INC_DIR += $(REP_DIR)/src/drivers/ahci/spec/x86
|
||||
|
||||
include $(REP_DIR)/src/drivers/ahci/target.inc
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
SRC_CC += main.cc ahci.cc platform.cc
|
||||
SRC_CC += main.cc platform.cc
|
||||
INC_DIR += $(REP_DIR)/src/drivers/ahci
|
||||
LIBS += base
|
||||
|
||||
|
71
repos/os/src/drivers/ahci/util.h
Normal file
71
repos/os/src/drivers/ahci/util.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* \brief Utilitize used by the AHCI driver
|
||||
* \author Josef Soentgen
|
||||
* \date 2018-03-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 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 _AHCI_UTIL_H_
|
||||
#define _AHCI_UTIL_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/fixed_stdint.h>
|
||||
|
||||
namespace Util {
|
||||
|
||||
using namespace Genode;
|
||||
/*
|
||||
* Wrap array into convinient interface
|
||||
*
|
||||
* The used datatype T must implement the following methods:
|
||||
*
|
||||
* bool valid() const returns true if the object is valid
|
||||
* void invalidate() adjusts the object so that valid() returns false
|
||||
*/
|
||||
template <typename T, size_t CAP>
|
||||
struct Slots
|
||||
{
|
||||
T _entries[CAP] { };
|
||||
size_t _limit { CAP };
|
||||
|
||||
/**
|
||||
* Get free slot
|
||||
*/
|
||||
T *get()
|
||||
{
|
||||
for (size_t i = 0; i < _limit; i++) {
|
||||
if (!_entries[i].valid()) { return &_entries[i]; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all slots until FUNC returns true
|
||||
*/
|
||||
template <typename FUNC>
|
||||
bool for_each(FUNC const &func)
|
||||
{
|
||||
for (size_t i = 0; i < _limit; i++) {
|
||||
if (!_entries[i].valid()) { continue; }
|
||||
if ( func(_entries[i])) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t index(T const &entry) const
|
||||
{
|
||||
size_t index = &entry - _entries;
|
||||
return index;
|
||||
}
|
||||
|
||||
void limit(size_t limit) { _limit = limit; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _AHCI_UTIL_H_ */
|
Loading…
Reference in New Issue
Block a user