mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-29 15:44:02 +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">
|
<start name="ahci_drv">
|
||||||
<resource name="RAM" quantum="10M" />
|
<resource name="RAM" quantum="10M" />
|
||||||
<provides><service name="Block" /></provides>
|
<provides><service name="Block" /></provides>
|
||||||
<config>
|
<config atapi="yes">
|
||||||
<report ports="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>
|
</config>
|
||||||
<route>
|
<route>
|
||||||
<service name="Report"> <child name="ahci_report_rom"/> </service>
|
<service name="Report"> <child name="ahci_report_rom"/> </service>
|
||||||
@ -80,6 +81,15 @@ append config {
|
|||||||
<any-service> <parent/> <any-child /> </any-service>
|
<any-service> <parent/> <any-child /> </any-service>
|
||||||
</route>
|
</route>
|
||||||
</start>
|
</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> }
|
</config> }
|
||||||
|
|
||||||
install_config $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 " -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=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=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
|
||||||
|
run_genode_until "Tests finished successfully!" 100 [output_spawn_id]
|
||||||
|
|
||||||
exec rm -f bin/ext2.raw
|
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
|
* \author Sebastian Sumpf
|
||||||
* \date 2015-04-29
|
* \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
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _ATAPI_DRIVER_H_
|
#ifndef _AHCI__ATAPI_PROTOCOL_H_
|
||||||
#define _ATAPI_DRIVER_H_
|
#define _AHCI__ATAPI_PROTOCOL_H_
|
||||||
|
|
||||||
#include "ahci.h"
|
#include "ahci.h"
|
||||||
#include <util/endian.h>
|
#include <util/endian.h>
|
||||||
|
|
||||||
|
namespace Atapi {
|
||||||
|
class Protocol;
|
||||||
|
using namespace Ahci;
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
}
|
||||||
|
|
||||||
struct Atapi_driver : Port_driver
|
class Atapi::Protocol : public Ahci::Protocol, Noncopyable
|
||||||
{
|
{
|
||||||
unsigned sense_tries = 0;
|
private:
|
||||||
Block::Packet_descriptor pending { };
|
|
||||||
|
|
||||||
Atapi_driver(Genode::Ram_allocator &ram,
|
Block::Request _pending { };
|
||||||
Ahci_root &root,
|
block_number_t _block_count { 0 };
|
||||||
unsigned &sem,
|
size_t _block_size { 2048 };
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void atapi_command()
|
void _atapi_command(Port &port)
|
||||||
{
|
{
|
||||||
Command_header header(command_header_addr(0));
|
Command_header header(port.command_header_addr(0));
|
||||||
header.atapi_command();
|
header.atapi_command();
|
||||||
header.clear_byte_count();
|
header.clear_byte_count();
|
||||||
execute(0);
|
port.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
Command_table table(port.command_table_addr(0), phys, 0x1000);
|
||||||
table.fis.atapi();
|
table.fis.atapi();
|
||||||
table.atapi_cmd.read_sense();
|
table.atapi_cmd.read_sense();
|
||||||
|
|
||||||
atapi_command();
|
_atapi_command(port);
|
||||||
}
|
|
||||||
|
|
||||||
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>()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)) {
|
_atapi_command(port);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == READY && Port::Is::Dhrs::get(status)) {
|
void _read_capacity(Port &port)
|
||||||
ack_packets();
|
{
|
||||||
|
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)) {
|
void _start_unit(Port &port)
|
||||||
switch (state) {
|
{
|
||||||
|
Command_table table(port.command_table_addr(0), 0, 0);
|
||||||
|
table.fis.atapi();
|
||||||
|
table.atapi_cmd.start_unit();
|
||||||
|
|
||||||
case STATUS:
|
_atapi_command(port);
|
||||||
test_unit_ready();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IDENTIFY:
|
|
||||||
state = READY;
|
|
||||||
state_change();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case READY:
|
|
||||||
ack_packets();
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Block::Session::Info info() const override
|
public:
|
||||||
{
|
|
||||||
return { .block_size = block_size(),
|
|
||||||
.block_count = block_count(),
|
|
||||||
.align_log2 = 11,
|
|
||||||
.writeable = false };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
** Ahci::Protocol interface **
|
||||||
|
******************************/
|
||||||
|
|
||||||
/*****************************
|
unsigned init(Port &port) override
|
||||||
** Block::Driver interface **
|
{
|
||||||
*****************************/
|
port.write<Port::Cmd::Atapi>(1);
|
||||||
|
|
||||||
bool dma_enabled() override { return true; };
|
retry<Port::Polling_timeout>(
|
||||||
|
[&] {
|
||||||
|
|
||||||
Genode::size_t block_size() const override
|
_start_unit(port);
|
||||||
{
|
port.wait_for_any(port.hba.delayer(),
|
||||||
return host_to_big_endian(((unsigned *)device_info)[1]);
|
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
|
/* read sense */
|
||||||
{
|
_read_sense(port);
|
||||||
return host_to_big_endian(((unsigned *)device_info)[0]) + 1;
|
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,
|
/* test unit ready */
|
||||||
size_t count,
|
_test_unit_ready(port);
|
||||||
addr_t phys,
|
port.wait_for(port.hba.delayer(), Port::Is::Dhrs::Equal(1));
|
||||||
Block::Packet_descriptor &packet) override
|
port.ack_irq();
|
||||||
{
|
|
||||||
if (pending.size())
|
|
||||||
throw Block::Driver::Request_congestion();
|
|
||||||
|
|
||||||
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)
|
_block_count = host_to_big_endian(((unsigned *)port.device_info)[0]) + 1;
|
||||||
Genode::log("add packet read ", block_number, " count ", count, " -> 0");
|
_block_size = host_to_big_endian(((unsigned *)port.device_info)[1]);
|
||||||
|
},
|
||||||
|
[&] {}, 3);
|
||||||
|
|
||||||
/* setup fis */
|
return 1;
|
||||||
Command_table table(command_table_addr(0), phys, count * block_size());
|
}
|
||||||
table.fis.atapi();
|
|
||||||
|
|
||||||
/* setup atapi command */
|
Block::Session::Info info() const override
|
||||||
table.atapi_cmd.read10(block_number, count);
|
{
|
||||||
|
return { .block_size = _block_size,
|
||||||
|
.block_count = _block_count,
|
||||||
|
.align_log2 = 1,
|
||||||
|
.writeable = false };
|
||||||
|
}
|
||||||
|
|
||||||
/* set and clear write flag in command header */
|
void handle_irq(Port &port) override
|
||||||
Command_header header(command_header_addr(0));
|
{
|
||||||
header.write<Command_header::Bits::W>(0);
|
port.ack_irq();
|
||||||
header.clear_byte_count();
|
}
|
||||||
|
|
||||||
/* set pending */
|
void writeable(bool) override { }
|
||||||
execute(0);
|
|
||||||
}
|
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
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
@ -14,195 +14,394 @@
|
|||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <base/attached_rom_dataspace.h>
|
#include <base/attached_rom_dataspace.h>
|
||||||
#include <base/component.h>
|
#include <base/component.h>
|
||||||
#include <base/heap.h>
|
|
||||||
#include <base/log.h>
|
#include <base/log.h>
|
||||||
#include <block/component.h>
|
#include <block/request_stream.h>
|
||||||
#include <os/session_policy.h>
|
#include <os/session_policy.h>
|
||||||
|
#include <timer_session/connection.h>
|
||||||
#include <util/xml_node.h>
|
#include <util/xml_node.h>
|
||||||
#include <os/reporter.h>
|
#include <os/reporter.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <ahci.h>
|
#include <ahci.h>
|
||||||
|
#include <ata_protocol.h>
|
||||||
|
#include <atapi_protocol.h>
|
||||||
|
|
||||||
|
namespace Ahci {
|
||||||
namespace Block {
|
struct Dispatch;
|
||||||
class Factory;
|
class Driver;
|
||||||
class Root_multiple_clients;
|
struct Main;
|
||||||
class Main;
|
struct Block_session_handler;
|
||||||
|
struct Block_session_component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Block::Factory : Driver_factory
|
struct Ahci::Dispatch : Interface
|
||||||
{
|
{
|
||||||
long device_num;
|
virtual void session(unsigned index) = 0;
|
||||||
|
|
||||||
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) { }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Session_component : public Block::Session_component
|
class Ahci::Driver : Noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Session_component(Block::Driver_factory &driver_factory,
|
enum { MAX_PORTS = 32 };
|
||||||
Genode::Entrypoint &ep,
|
|
||||||
Genode::Region_map &rm,
|
|
||||||
Genode::size_t buf_size,
|
|
||||||
bool writeable)
|
|
||||||
: Block::Session_component(driver_factory, ep, rm, buf_size, writeable) { }
|
|
||||||
|
|
||||||
Block::Driver_factory &factory() { return _driver_factory; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Block::Root_multiple_clients : public Root_component< ::Session_component>,
|
|
||||||
public Ahci_root
|
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Genode::Env &_env;
|
Env &_env;
|
||||||
Genode::Allocator &_alloc;
|
Dispatch &_dispatch;
|
||||||
Genode::Xml_node _config;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
::Session_component *_create_session(const char *args) override
|
struct Timer_delayer : Mmio::Delayer, Timer::Connection
|
||||||
{
|
{
|
||||||
Session_label const label = label_from_args(args);
|
Timer_delayer(Env &env)
|
||||||
Session_policy const policy(label, _config);
|
: Timer::Connection(env) { }
|
||||||
|
|
||||||
size_t ram_quota =
|
void usleep(uint64_t us) override { Timer::Connection::usleep(us); }
|
||||||
Arg_string::find_arg(args, "ram_quota").ulong_value(0);
|
} _delayer { _env };
|
||||||
size_t tx_buf_size =
|
|
||||||
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
|
|
||||||
|
|
||||||
if (!tx_buf_size)
|
Hba _hba { _env, _delayer };
|
||||||
throw Service_denied();
|
|
||||||
|
|
||||||
size_t session_size = sizeof(::Session_component)
|
Constructible<Ata::Protocol> _ata[MAX_PORTS];
|
||||||
+ sizeof(Factory) + tx_buf_size;
|
Constructible<Atapi::Protocol> _atapi[MAX_PORTS];
|
||||||
|
Constructible<Port> _ports[MAX_PORTS];
|
||||||
|
|
||||||
if (max((size_t)4096, session_size) > ram_quota) {
|
Signal_handler<Driver> _irq { _env.ep(), *this, &Driver::handle_irq };
|
||||||
error("insufficient 'ram_quota' from '", label, "',"
|
bool _enable_atapi;
|
||||||
" got ", ram_quota, ", need ", session_size);
|
|
||||||
throw Insufficient_ram_quota();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* try read device port number attribute */
|
void _info()
|
||||||
long num = policy.attribute_value("device", -1L);
|
{
|
||||||
|
log("version: "
|
||||||
/* try read device model and serial number attributes */
|
"major=", Hex(_hba.read<Hba::Version::Major>()), " "
|
||||||
auto const model = policy.attribute_value("model", String<64>());
|
"minor=", Hex(_hba.read<Hba::Version::Minor>()));
|
||||||
auto const serial = policy.attribute_value("serial", String<64>());
|
log("command slots: ", _hba.command_slots());
|
||||||
|
log("native command queuing: ", _hba.ncq() ? "yes" : "no");
|
||||||
/* sessions are not writeable by default */
|
log("64-bit support: ", _hba.supports_64bit() ? "yes" : "no");
|
||||||
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 _destroy_session(::Session_component *session) override
|
void _scan_ports(Region_map &rm)
|
||||||
{
|
{
|
||||||
Driver_factory &factory = session->factory();
|
log("number of ports: ", _hba.port_count(), " pi: ",
|
||||||
Genode::destroy(&_alloc, session);
|
Hex(_hba.read<Hba::Pi>()));
|
||||||
Genode::destroy(&_alloc, &factory);
|
|
||||||
|
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:
|
public:
|
||||||
|
|
||||||
Root_multiple_clients(Genode::Env &env, Genode::Allocator &alloc,
|
Driver(Env &env, Dispatch &dispatch, bool support_atapi)
|
||||||
Genode::Xml_node config)
|
: _env(env), _dispatch(dispatch), _enable_atapi(support_atapi)
|
||||||
:
|
|
||||||
Root_component(&env.ep().rpc_ep(), &alloc),
|
|
||||||
_env(env), _alloc(alloc), _config(config)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Genode::Entrypoint &entrypoint() override { return _env.ep(); }
|
|
||||||
|
|
||||||
void announce() override
|
|
||||||
{
|
{
|
||||||
_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;
|
Env &env;
|
||||||
Genode::Heap heap { env.ram(), env.rm() };
|
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;
|
~Block_session_handler()
|
||||||
|
|
||||||
Signal_handler<Main> device_identified {
|
|
||||||
env.ep(), *this, &Main::handle_device_identified };
|
|
||||||
|
|
||||||
Main(Genode::Env &env)
|
|
||||||
: env(env), root(env, heap, config.xml())
|
|
||||||
{
|
{
|
||||||
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);
|
bool support_atapi = config.xml().attribute_value("atapi", false);
|
||||||
try {
|
try {
|
||||||
Ahci_driver::init(env, heap, root, support_atapi, device_identified);
|
driver.construct(env, *this, support_atapi);
|
||||||
} catch (Ahci_driver::Missing_controller) {
|
report_ports();
|
||||||
Genode::error("no AHCI controller found");
|
} catch (Ahci::Missing_controller) {
|
||||||
|
error("no AHCI controller found");
|
||||||
env.parent().exit(~0);
|
env.parent().exit(~0);
|
||||||
} catch (Genode::Service_denied) {
|
} catch (Service_denied) {
|
||||||
Genode::error("hardware access denied");
|
error("hardware access denied");
|
||||||
env.parent().exit(~0);
|
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 {
|
try {
|
||||||
Xml_node report = config.xml().sub_node("report");
|
Xml_node report = config.xml().sub_node("report");
|
||||||
if (report.attribute_value("ports", false)) {
|
if (report.attribute_value("ports", false)) {
|
||||||
reporter.construct(env, "ports");
|
reporter.construct(env, "ports");
|
||||||
reporter->enabled(true);
|
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 Ahci::Main server(env); }
|
||||||
void Component::construct(Genode::Env &env) { static Block::Main server(env); }
|
|
||||||
|
@ -1,156 +1,87 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Driver for PCI-bus platforms
|
* \brief Driver for PCI-bus platforms
|
||||||
* \author Sebastian Sumpf
|
* \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
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* 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>
|
#include <ahci.h>
|
||||||
|
|
||||||
|
Ahci::Data::Data(Env &env)
|
||||||
using namespace Genode;
|
: env(env)
|
||||||
|
|
||||||
struct X86_hba : Platform::Hba
|
|
||||||
{
|
{
|
||||||
enum Pci_config {
|
pci_device_cap = pci.with_upgrade(
|
||||||
CLASS_MASS_STORAGE = 0x10000u,
|
[&] () { return pci.next_device(pci_device_cap, AHCI_DEVICE,
|
||||||
SUBCLASS_AHCI = 0x600u,
|
CLASS_MASK); });
|
||||||
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;
|
if (!pci_device_cap.valid()) {
|
||||||
|
throw Missing_controller();
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void disable_msi()
|
/* construct pci client */
|
||||||
{
|
pci_device.construct(pci_device_cap);
|
||||||
enum { PM_CAP_OFF = 0x34, MSI_CAP = 0x5, MSI_ENABLED = 0x1 };
|
log("AHCI found ("
|
||||||
uint8_t cap = pci_device->config_read(PM_CAP_OFF, Platform::Device::ACCESS_8BIT);
|
"vendor: ", Hex(pci_device->vendor_id()), " "
|
||||||
|
"device: ", Hex(pci_device->device_id()), " "
|
||||||
|
"class: ", Hex(pci_device->class_code()), ")");
|
||||||
|
|
||||||
/* iterate through cap pointers */
|
/* map base address of controller */
|
||||||
for (uint16_t val = 0; cap; cap = val >> 8) {
|
Io_mem_session_capability iomem_cap = pci_device->io_mem(pci_device->phys_bar_to_virt(AHCI_BASE_ID));
|
||||||
val = pci_device->config_read(cap, Platform::Device::ACCESS_16BIT);
|
iomem.construct(env.rm(), Io_mem_session_client(iomem_cap).dataspace());
|
||||||
|
|
||||||
if ((val & 0xff) != MSI_CAP)
|
uint16_t cmd = pci_device->config_read(PCI_CMD, ::Platform::Device::ACCESS_16BIT);
|
||||||
continue;
|
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);
|
irq.construct(pci_device->irq(0));
|
||||||
|
}
|
||||||
if (msi & MSI_ENABLED) {
|
|
||||||
_config_write(cap + 2, msi ^ MSI_CAP,
|
|
||||||
Platform::Device::ACCESS_8BIT);
|
/************************
|
||||||
Genode::log("disabled MSI ", msi);
|
** Platform interface **
|
||||||
}
|
************************/
|
||||||
}
|
|
||||||
}
|
Genode::addr_t Ahci::Platform::_mmio_base() const
|
||||||
|
{
|
||||||
void _config_write(uint8_t op, uint16_t cmd,
|
return addr_t(_data.iomem->local_addr<addr_t>());
|
||||||
Platform::Device::Access_size width)
|
}
|
||||||
{
|
|
||||||
Genode::size_t donate = 4096;
|
|
||||||
Genode::retry<Platform::Out_of_ram>(
|
void Ahci::Platform::sigh_irq(Signal_context_capability sigh)
|
||||||
[&] () {
|
{
|
||||||
Genode::retry<Platform::Out_of_caps>(
|
_data.irq->sigh(sigh);
|
||||||
[&] () { pci_device->config_write(op, cmd, width); },
|
ack_irq();
|
||||||
[&] () { pci.upgrade_caps(2); });
|
}
|
||||||
},
|
|
||||||
[&] () {
|
|
||||||
pci.upgrade_ram(donate);
|
void Ahci::Platform::ack_irq() { _data.irq->ack_irq(); }
|
||||||
donate *= 2;
|
|
||||||
});
|
|
||||||
}
|
Genode::Ram_dataspace_capability Ahci::Platform::alloc_dma_buffer(size_t size)
|
||||||
|
{
|
||||||
|
size_t donate = size;
|
||||||
/*******************
|
|
||||||
** Hba interface **
|
return retry<Genode::Out_of_ram>(
|
||||||
*******************/
|
[&] () {
|
||||||
|
return retry<Genode::Out_of_caps>(
|
||||||
Genode::addr_t base() const override { return res_base; }
|
[&] () { return _data.pci.alloc_dma_buffer(size); },
|
||||||
Genode::size_t size() const override { return res_size; }
|
[&] () { _data.pci.upgrade_caps(2); });
|
||||||
|
},
|
||||||
void sigh_irq(Signal_context_capability sigh) override
|
[&] () {
|
||||||
{
|
_data.pci.upgrade_ram(donate);
|
||||||
irq->sigh(sigh);
|
donate = donate * 2 > size ? 4096 : donate * 2;
|
||||||
ack_irq();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ack_irq() override { irq->ack_irq(); }
|
|
||||||
|
void Ahci::Platform::free_dma_buffer(Genode::Ram_dataspace_capability ds)
|
||||||
Ram_dataspace_capability
|
{
|
||||||
alloc_dma_buffer(Genode::size_t size) override
|
_data.pci.free_dma_buffer(ds);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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
|
TARGET = ahci_drv
|
||||||
REQUIRES = x86
|
REQUIRES = x86
|
||||||
|
|
||||||
|
INC_DIR += $(REP_DIR)/src/drivers/ahci/spec/x86
|
||||||
|
|
||||||
include $(REP_DIR)/src/drivers/ahci/target.inc
|
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
|
INC_DIR += $(REP_DIR)/src/drivers/ahci
|
||||||
LIBS += base
|
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…
x
Reference in New Issue
Block a user