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:
Sebastian Sumpf 2020-01-08 12:58:32 +01:00 committed by Christian Helmuth
parent 73f2c7043c
commit 8a7deae238
12 changed files with 1471 additions and 1510 deletions

View File

@ -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

View File

@ -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());
}
});
}
});
}

View File

@ -1,4 +1,5 @@
/**
*
* \brief Generic AHCI controller definitions
* \author Sebasitan Sumpf
* \date 2015-04-29
@ -11,77 +12,73 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__AHCI_H_
#define _INCLUDE__AHCI_H_
#ifndef _AHCI__AHCI_H_
#define _AHCI__AHCI_H_
#include <block/component.h>
#include <block/request_stream.h>
#include <os/attached_mmio.h>
#include <os/reporter.h>
#include <util/retry.h>
#include <util/reconstructible.h>
#include <platform.h>
static bool constexpr verbose = false;
namespace Platform {
namespace Ahci {
struct Missing_controller : Exception { };
class Platform;
struct Protocol;
struct Port;
struct Port_base;
struct Hba;
Hba &init(Genode::Env &env, Genode::Mmio::Delayer &delayer);
};
struct Ahci_root : Genode::Interface
{
virtual Genode::Entrypoint &entrypoint() = 0;
virtual void announce() = 0;
};
namespace Ahci_driver {
void init(Genode::Env &env, Genode::Allocator &alloc, Ahci_root &ep,
bool support_atapi, Genode::Signal_context_capability device_identified);
bool avail(long device_num);
long device_number(char const *model_num, char const *serial_num);
Block::Driver *claim_port(long device_num);
void free_port(long device_num);
void report_ports(Genode::Reporter &reporter);
struct Missing_controller { };
using Response = Block::Request_stream::Response;
using block_number_t = Block::block_number_t;
using block_count_t = Block::block_count_t;
}
struct Platform::Hba : Genode::Interface
class Ahci::Platform
{
private :
Data _data;
protected:
/**
* Return base address and size of HBA device registers
*/
virtual Genode::addr_t base() const = 0;
virtual Genode::size_t size() const = 0;
addr_t _mmio_base() const;
public:
Platform(Env &env) : _data(env) { };
/**
* Register interrupt signal context
*/
virtual void sigh_irq(Genode::Signal_context_capability sigh) = 0;
virtual void ack_irq() = 0;
void sigh_irq(Signal_context_capability sigh);
void ack_irq();
/**
* DMA
*/
virtual Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) = 0;
virtual void free_dma_buffer(Genode::Ram_dataspace_capability ds) = 0;
Ram_dataspace_capability alloc_dma_buffer(size_t size);
void free_dma_buffer(Ram_dataspace_capability ds);
};
/**
* HBA definitions
*/
struct Hba : Genode::Attached_mmio
struct Ahci::Hba : Ahci::Platform,
Mmio
{
Mmio::Delayer &_delayer;
Hba(Genode::Env &env, Platform::Hba &hba, Mmio::Delayer &delayer)
: Attached_mmio(env, hba.base(), hba.size()), _delayer(delayer) { }
Hba(Env &env, Mmio::Delayer &delayer)
: Platform(env), Mmio(_mmio_base()), _delayer(delayer) { }
/**
* Host capabilites
@ -115,7 +112,11 @@ struct Hba : Genode::Attached_mmio
*/
struct Is : Register<0x8, 32> { };
void ack_irq() { write<Is>(read<Is>()); }
void ack_irq()
{
write<Is>(read<Is>());
Platform::ack_irq();
}
/**
* Ports implemented
@ -146,7 +147,13 @@ struct Hba : Genode::Attached_mmio
};
struct Device_fis : Genode::Mmio
/***********************************
** AHCI commands and descriptors **
***********************************/
namespace Ahci {
struct Device_fis : Mmio
{
struct Status : Register<0x2, 8>
{
@ -155,12 +162,12 @@ struct Device_fis : Genode::Mmio
};
struct Error : Register<0x3, 8> { };
Device_fis(Genode::addr_t recv_base)
Device_fis(addr_t recv_base)
: Mmio(recv_base + 0x40) { }
};
struct Command_fis : Genode::Mmio
struct Command_fis : Mmio
{
struct Type : Register<0x0, 8> { }; /* FIS type */
struct Bits : Register<0x1, 8, 1>
@ -174,7 +181,7 @@ struct Command_fis : Genode::Mmio
struct Lba0_7 : Register<0x4, 8> { };
struct Lba8_15 : Register<0x5, 8> { };
struct Lba16_23 : Register<0x6, 8> { };
struct Lba24 : Genode::Bitset_3<Lba0_7, Lba8_15, Lba16_23> { };
struct Lba24 : Bitset_3<Lba0_7, Lba8_15, Lba16_23> { };
struct Device : Register<0x7, 8>
{
struct Lba : Bitfield<6, 1> { }; /* enable LBA mode */
@ -185,9 +192,9 @@ struct Command_fis : Genode::Mmio
struct Lba32_39 : Register<0x9, 8> { };
struct Lba40_47 : Register<0xa, 8> { };
struct Features8_15 : Register<0xb, 8> { };
struct Features : Genode::Bitset_2<Features0_7, Features8_15> { };
struct Lba48 : Genode::Bitset_3<Lba24_31, Lba32_39, Lba40_47> { };
struct Lba : Genode::Bitset_2<Lba24, Lba48> { }; /* LBA 0 - 47 */
struct Features : Bitset_2<Features0_7, Features8_15> { };
struct Lba48 : Bitset_3<Lba24_31, Lba32_39, Lba40_47> { };
struct Lba : Bitset_2<Lba24, Lba48> { }; /* LBA 0 - 47 */
/* big endian */
struct Sector0_7 : Register<0xc, 8, 1>
@ -195,9 +202,9 @@ struct Command_fis : Genode::Mmio
struct Tag : Bitfield<3, 5> { };
};
struct Sector8_15 : Register<0xd, 8> { };
struct Sector : Genode::Bitset_2<Sector0_7, Sector8_15> { }; /* sector count */
struct Sector : Bitset_2<Sector0_7, Sector8_15> { }; /* sector count */
Command_fis(Genode::addr_t base)
Command_fis(addr_t base)
: Mmio(base)
{
clear();
@ -206,8 +213,8 @@ struct Command_fis : Genode::Mmio
write<Type>(HOST_TO_DEVICE);
}
static constexpr Genode::size_t size() { return 0x14; }
void clear() { Genode::memset((void *)base(), 0, size()); }
static constexpr size_t size() { return 0x14; }
void clear() { memset((void *)base(), 0, size()); }
/************************
@ -221,7 +228,7 @@ struct Command_fis : Genode::Mmio
write<Command>(0xec);
}
void dma_ext(bool read, Block::sector_t block_number, Genode::size_t block_count)
void dma_ext(bool read, block_number_t block_number, block_count_t block_count)
{
write<Bits::C>(1);
write<Device::Lba>(1);
@ -231,7 +238,7 @@ struct Command_fis : Genode::Mmio
write<Sector>(block_count);
}
void fpdma(bool read, Block::sector_t block_number, Genode::size_t block_count,
void fpdma(bool read, block_number_t block_number, block_count_t block_count,
unsigned slot)
{
write<Bits::C>(1);
@ -249,13 +256,22 @@ struct Command_fis : Genode::Mmio
/* packet command */
write<Command>(0xa0);
}
/*
* Sets byte count limit for PIO transfers
*/
void byte_count(uint16_t bytes)
{
write<Lba8_15>(bytes & 0xff);
write<Lba16_23>(bytes >> 8);
}
};
/**
* AHCI command list structure header
*/
struct Command_header : Genode::Mmio
struct Command_header : Mmio
{
struct Bits : Register<0x0, 16>
{
@ -271,11 +287,11 @@ struct Command_header : Genode::Mmio
struct Ctba0 : Register<0x8, 32> { }; /* command table base addr (low) */
struct Ctba0_u0 : Register<0xc, 32> { }; /* command table base addr (upper) */
Command_header(Genode::addr_t base) : Mmio(base) { }
Command_header(addr_t base) : Mmio(base) { }
void cmd_table_base(Genode::addr_t base_phys)
void cmd_table_base(addr_t base_phys)
{
Genode::uint64_t addr = base_phys;
uint64_t addr = base_phys;
write<Ctba0>(addr);
write<Ctba0_u0>(addr >> 32);
write<Prdtl>(1);
@ -292,14 +308,14 @@ struct Command_header : Genode::Mmio
write<Bits::A>(1);
}
static constexpr Genode::size_t size() { return 0x20; }
static constexpr size_t size() { return 0x20; }
};
/**
* ATAPI packet 12 or 16 bytes
*/
struct Atapi_command : Genode::Mmio
struct Atapi_command : Mmio
{
struct Command : Register<0, 8> { };
@ -308,18 +324,18 @@ struct Atapi_command : Genode::Mmio
struct Lba16_23 : Register<0x3, 8> { };
struct Lba8_15 : Register<0x4, 8> { };
struct Lba0_7 : Register<0x5, 8> { };
struct Lba24 : Genode::Bitset_3<Lba0_7, Lba8_15, Lba16_23> { };
struct Lba : Genode::Bitset_2<Lba24, Lba24_31> { };
struct Lba24 : Bitset_3<Lba0_7, Lba8_15, Lba16_23> { };
struct Lba : Bitset_2<Lba24, Lba24_31> { };
/* sector count big endian */
struct Sector8_15 : Register<0x8, 8> { };
struct Sector0_7 : Register<0x9, 8> { };
struct Sector : Genode::Bitset_2<Sector0_7, Sector8_15> { };
struct Sector : Bitset_2<Sector0_7, Sector8_15> { };
Atapi_command(Genode::addr_t base) : Mmio(base)
Atapi_command(addr_t base) : Mmio(base)
{
Genode::memset((void *)base, 0, 16);
memset((void *)base, 0, 16);
}
void read_capacity()
@ -338,19 +354,24 @@ struct Atapi_command : Genode::Mmio
write<Lba8_15>(18);
}
void read10(Block::sector_t block_number, Genode::size_t block_count)
void read10(block_number_t block_number, block_count_t block_count)
{
write<Command>(0x28);
write<Lba>(block_number);
write<Sector>(block_count);
}
void start_unit()
{
write<Command>(0x1b);
}
};
/**
* Physical region descritpor table
*/
struct Prdt : Genode::Mmio
struct Prdt : Mmio
{
struct Dba : Register<0x0, 32> { }; /* data base address */
struct Dbau : Register<0x4, 32> { }; /* data base address upper 32 bits */
@ -361,16 +382,16 @@ struct Prdt : Genode::Mmio
struct Irq : Bitfield<31,1> { }; /* interrupt completion */
};
Prdt(Genode::addr_t base, Genode::addr_t phys, Genode::size_t bytes)
Prdt(addr_t base, addr_t phys, size_t bytes)
: Mmio(base)
{
Genode::uint64_t addr = phys;
uint64_t addr = phys;
write<Dba>(addr);
write<Dbau>(addr >> 32);
write<Bits::Dbc>(bytes > 0 ? bytes - 1 : 0);
}
static constexpr Genode::size_t size() { return 0x10; }
static constexpr size_t size() { return 0x10; }
};
@ -382,97 +403,144 @@ struct Command_table
/* in Genode we only need one PRD (for one packet) */
Prdt prdt;
Command_table(Genode::addr_t base,
Genode::addr_t phys,
Genode::size_t bytes = 0)
Command_table(addr_t base,
addr_t phys,
size_t bytes = 0)
: fis(base), atapi_cmd(base + 0x40),
prdt(base + 0x80, phys, bytes)
{ }
static constexpr Genode::size_t size() { return 0x100; }
static constexpr size_t size() { return 0x100; }
};
} /* namespace Ahci */
/**
* Minimalistic AHCI port structure to merely detect device signature
*/
struct Port_base : Genode::Mmio
struct Ahci::Port_base : Mmio
{
/* device signature */
enum Signature {
ATA_SIG = 0x101,
ATAPI_SIG = 0xeb140101,
ATAPI_SIG_QEMU = 0xeb140000, /* will be fixed in Qemu */
};
unsigned index { };
Hba &hba;
/**
* Port signature
*/
struct Sig : Register<0x24, 32> { };
static constexpr Genode::addr_t offset() { return 0x100; }
static constexpr Genode::size_t size() { return 0x80; }
static constexpr addr_t offset() { return 0x100; }
static constexpr size_t size() { return 0x80; }
Port_base(unsigned number, Hba &hba)
: Mmio(hba.base() + offset() + (number * size())) { }
Port_base(unsigned index, Hba &hba)
: Mmio(hba.base() + offset() + (index * size())),
index(index), hba(hba) { }
bool implemented() const
{
return hba.read<Hba::Pi>() & (1u << index);
}
bool ata() const { return read<Sig>() == ATA_SIG; }
bool atapi() const
{
unsigned sig = read<Sig>();
return sig == ATAPI_SIG || sig == ATAPI_SIG_QEMU;
}
};
struct Ahci::Protocol : Interface
{
virtual unsigned init(Port &port) = 0;
virtual Block::Session::Info info() const = 0;
virtual void handle_irq(Port &port) = 0;
virtual Response submit(Port &port, Block::Request const request) = 0;
virtual Block::Request completed(Port &port) = 0;
virtual void writeable(bool rw) = 0;
};
/**
* AHCI port
*/
struct Port : private Port_base
struct Ahci::Port : private Port_base
{
using Port_base::write;
using Port_base::read;
using Port_base::wait_for_any;
using Port_base::wait_for;
using Port_base::Register_set::Polling_timeout;
using Port_base::index;
using Port_base::hba;
struct Not_ready : Genode::Exception { };
struct Not_ready : Exception { };
Genode::Region_map &rm;
Hba &hba;
Platform::Hba &platform_hba;
Protocol &protocol;
Region_map &rm;
unsigned cmd_slots = hba.command_slots();
Genode::Ram_dataspace_capability device_ds { };
Genode::Ram_dataspace_capability cmd_ds { };
Genode::Ram_dataspace_capability device_info_ds { };
Ram_dataspace_capability device_ds { };
Ram_dataspace_capability cmd_ds { };
Ram_dataspace_capability device_info_ds { };
Genode::addr_t cmd_list = 0;
Genode::addr_t fis_base = 0;
Genode::addr_t cmd_table = 0;
Genode::addr_t device_info = 0;
addr_t cmd_list = 0;
addr_t fis_base = 0;
addr_t cmd_table = 0;
addr_t device_info = 0;
addr_t dma_base = 0; /* physical address of DMA memory */
enum State {
NONE,
STATUS,
TEST_READY,
IDENTIFY,
READY,
};
State state = NONE;
Port(Genode::Region_map &rm, Hba &hba, Platform::Hba &platform_hba,
unsigned number)
Port(Protocol &protocol, Region_map &rm, Hba &hba,
unsigned index)
:
Port_base(number, hba), rm(rm), hba(hba),
platform_hba(platform_hba)
Port_base(index, hba),
protocol(protocol), rm(rm)
{
reset();
if (!enable())
throw 1;
stop();
wait_for(hba.delayer(), Cmd::Cr::Equal(0));
init();
/*
* Init protocol and determine actual number of command slots of device
*/
try {
unsigned device_slots = protocol.init(*this);
cmd_slots = min(device_slots, cmd_slots);
} catch (Polling_timeout) {
/* ack any pending IRQ from failed device initialization */
ack_irq();
throw;
}
}
virtual ~Port()
{
if (device_ds.valid()) {
rm.detach((void *)cmd_list);
platform_hba.free_dma_buffer(device_ds);
hba.free_dma_buffer(device_ds);
}
if (cmd_ds.valid()) {
rm.detach((void *)cmd_table);
platform_hba.free_dma_buffer(cmd_ds);
hba.free_dma_buffer(cmd_ds);
}
if (device_info_ds.valid()) {
rm.detach((void*)device_info);
platform_hba.free_dma_buffer(device_info_ds);
hba.free_dma_buffer(device_info_ds);
}
}
@ -486,9 +554,9 @@ struct Port : private Port_base
*/
struct Clbu : Register<0x4, 32> { };
void command_list_base(Genode::addr_t phys)
void command_list_base(addr_t phys)
{
Genode::uint64_t addr = phys;
uint64_t addr = phys;
write<Clb>(addr);
write<Clbu>(addr >> 32);
}
@ -503,9 +571,9 @@ struct Port : private Port_base
*/
struct Fbu : Register<0xc, 32> { };
void fis_rcv_base(Genode::addr_t phys)
void fis_rcv_base(addr_t phys)
{
Genode::uint64_t addr = phys;
uint64_t addr = phys;
write<Fb>(addr);
write<Fbu>(addr >> 32);
}
@ -589,6 +657,7 @@ struct Port : private Port_base
struct Sud : Bitfield<1, 1> { }; /* spin-up device */
struct Pod : Bitfield<2, 1> { }; /* power-up device */
struct Fre : Bitfield<4, 1> { }; /* FIS receive enable */
struct Fr : Bitfield<14, 1> { }; /* FIS receive running */
struct Cr : Bitfield<15, 1> { }; /* command list running */
struct Atapi : Bitfield<24, 1> { };
struct Icc : Bitfield<28, 4> { }; /* interface communication control */
@ -602,14 +671,14 @@ struct Port : private Port_base
try {
wait_for(hba.delayer(), Tfd::Sts_bsy::Equal(0));
} catch (Polling_timeout) {
Genode::error("HBA busy unable to start command processing.");
error("HBA busy unable to start command processing.");
return;
}
try {
wait_for(hba.delayer(), Tfd::Sts_drq::Equal(0));
} catch (Polling_timeout) {
Genode::error("HBA in DRQ unable to start command processing.");
error("HBA in DRQ unable to start command processing.");
return;
}
@ -672,7 +741,7 @@ struct Port : private Port_base
/* try to wake up device */
write<Cmd::Icc>(Ssts::Ipm::ACTIVE);
Genode::retry<Not_ready>(
retry<Not_ready>(
[&] {
if ((Ssts::Dec::get(status) != Ssts::Dec::ESTABLISHED) ||
!(Ssts::Ipm::get(status) & Ssts::Ipm::ACTIVE))
@ -700,7 +769,7 @@ struct Port : private Port_base
void reset()
{
if (read<Cmd::St>())
Genode::warning("CMD.ST bit set during device reset --> unknown behavior");
warning("CMD.ST bit set during device reset --> unknown behavior");
write<Sctl::Det>(1);
hba.delayer().usleep(1000);
@ -709,7 +778,7 @@ struct Port : private Port_base
try {
wait_for(hba.delayer(), Ssts::Dec::Equal(Ssts::Dec::ESTABLISHED));
} catch (Polling_timeout) {
Genode::warning("Port reset failed");
warning("Port reset failed");
}
}
@ -728,14 +797,14 @@ struct Port : private Port_base
void clear_serr() { write<Serr>(read<Serr>()); }
/**
* Serial ATA active
* Serial ATA active (strict write)
*/
struct Sact : Register<0x34, 32> { };
struct Sact : Register<0x34, 32, true> { };
/**
* Command issue
* Command issue (strict write)
*/
struct Ci : Register<0x38, 32> { };
struct Ci : Register<0x38, 32, true> { };
void init()
{
@ -767,25 +836,29 @@ struct Port : private Port_base
void setup_memory()
{
using Genode::addr_t;
using Genode::size_t;
device_ds = platform_hba.alloc_dma_buffer(0x1000);
device_ds = hba.alloc_dma_buffer(0x1000);
/* command list 1K */
addr_t phys = Genode::Dataspace_client(device_ds).phys_addr();
addr_t phys = Dataspace_client(device_ds).phys_addr();
cmd_list = (addr_t)rm.attach(device_ds);
command_list_base(phys);
/* receive FIS base 256 byte */
fis_base = cmd_list + 1024;
/*
* Set fis receive base, clear Fre (FIS receive) before and wait for FR
* (FIS receive running) to clear
*/
write<Cmd::Fre>(0);
wait_for(hba.delayer(), Cmd::Fr::Equal(0));
fis_rcv_base(phys + 1024);
/* command table */
size_t cmd_size = Genode::align_addr(cmd_slots * Command_table::size(), 12);
cmd_ds = platform_hba.alloc_dma_buffer(cmd_size);
size_t cmd_size = align_addr(cmd_slots * Command_table::size(), 12);
cmd_ds = hba.alloc_dma_buffer(cmd_size);
cmd_table = (addr_t)rm.attach(cmd_ds);
phys = (addr_t)Genode::Dataspace_client(cmd_ds).phys_addr();
phys = (addr_t)Dataspace_client(cmd_ds).phys_addr();
/* set command table addresses in command list */
for (unsigned i = 0; i < cmd_slots; i++) {
@ -794,16 +867,16 @@ struct Port : private Port_base
}
/* dataspace for device info */
device_info_ds = platform_hba.alloc_dma_buffer(0x1000);
device_info_ds = hba.alloc_dma_buffer(0x1000);
device_info = rm.attach(device_info_ds);
}
Genode::addr_t command_table_addr(unsigned slot)
addr_t command_table_addr(unsigned slot)
{
return cmd_table + (slot * Command_table::size());
};
Genode::addr_t command_header_addr(unsigned slot)
addr_t command_header_addr(unsigned slot)
{
return cmd_list + (slot * Command_header::size());
}
@ -814,68 +887,62 @@ struct Port : private Port_base
write<Ci>(1U << slot);
}
bool ready() { return state == READY; }
};
struct Port_driver : Port, Block::Driver
bool sanity_check(Block::Request const &request)
{
Ahci_root &root;
unsigned &sem;
Block::Session::Info const i = info();
Port_driver(Genode::Ram_allocator &ram,
Ahci_root &root,
unsigned &sem,
Genode::Region_map &rm,
Hba &hba,
Platform::Hba &platform_hba,
unsigned number)
: Port(rm, hba, platform_hba, number), Block::Driver(ram), root(root),
sem(sem) { sem++; }
virtual void handle_irq() = 0;
virtual Genode::size_t block_size() const = 0;
virtual Block::sector_t block_count() const = 0;
void state_change()
{
if (--sem) return;
/* announce service */
root.announce();
}
void sanity_check(Block::sector_t block_number, Genode::size_t count)
{
/* max. PRDT size is 4MB */
if (count * block_size() > 4 * 1024 * 1024) {
Genode::error("error: maximum supported packet size is 4MB");
throw Io_error();
if (request.operation.count * i.block_size > 4 * 1024 * 1024) {
error("error: maximum supported packet size is 4MB");
return false;
}
/* sanity check */
if (block_number + count > block_count()) {
Genode::error("error: requested blocks are outside of device");
throw Io_error();
}
if (request.operation.count + request.operation.block_number > i.block_count) {
error("error: requested blocks are outside of device");
return false;
}
return true;
}
/*******************
** Block::Driver **
*******************/
Genode::Ram_dataspace_capability
alloc_dma_buffer(Genode::size_t size) override
Ram_dataspace_capability alloc_buffer(size_t size)
{
return platform_hba.alloc_dma_buffer(size);
if (dma_base) return Ram_dataspace_capability();
Ram_dataspace_capability dma = hba.alloc_dma_buffer(size);
dma_base = Dataspace_client(dma).phys_addr();
return dma;
}
void free_dma_buffer(Genode::Ram_dataspace_capability c) override
void free_buffer(Ram_dataspace_capability ds)
{
platform_hba.free_dma_buffer(c);
dma_base = 0;
hba.free_dma_buffer(ds);
}
/**********************
** Protocol wrapper **
**********************/
Block::Session::Info info() const { return protocol.info(); }
void handle_irq() { protocol.handle_irq(*this); }
Response submit(Block::Request const request) {
return protocol.submit(*this, request); }
template <typename FN>
void for_one_completed_request(FN const &fn)
{
Block::Request request = protocol.completed(*this);
if (!request.operation.valid()) return;
request.success = true;
fn(request);
}
void writeable(bool rw) { protocol.writeable(rw); }
};
#endif /* _INCLUDE__AHCI_H_ */
#endif /* _AHCI__AHCI_H_ */

View File

@ -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_ */

View 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_ */

View File

@ -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
{
unsigned sense_tries = 0;
Block::Packet_descriptor pending { };
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();
}
void atapi_command()
class Atapi::Protocol : public Ahci::Protocol, Noncopyable
{
Command_header header(command_header_addr(0));
private:
Block::Request _pending { };
block_number_t _block_count { 0 };
size_t _block_size { 2048 };
void _atapi_command(Port &port)
{
Command_header header(port.command_header_addr(0));
header.atapi_command();
header.clear_byte_count();
execute(0);
port.execute(0);
}
void test_unit_ready()
void _read_sense(Port &port)
{
state = TEST_READY;
addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
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();
Command_table table(command_table_addr(0), phys, 0x1000);
Command_table table(port.command_table_addr(0), phys, 0x1000);
table.fis.atapi();
table.atapi_cmd.read_sense();
atapi_command();
_atapi_command(port);
}
void read_capacity()
void _test_unit_ready(Port &port)
{
state = IDENTIFY;
addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
Command_table table(command_table_addr(0), phys, 0x1000);
Command_table table(port.command_table_addr(0), 0, 0);
table.fis.atapi();
table.atapi_cmd.test_unit_ready();
_atapi_command(port);
}
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();
_atapi_command(port);
}
void ack_packets()
void _start_unit(Port &port)
{
unsigned slots = Port::read<Ci>();
Command_table table(port.command_table_addr(0), 0, 0);
table.fis.atapi();
table.atapi_cmd.start_unit();
if (slots & 1 || !pending.size())
return;
Block::Packet_descriptor p = pending;
pending = Block::Packet_descriptor();
ack_packet(p, true);
_atapi_command(port);
}
public:
/*****************
** Port_driver **
*****************/
/******************************
** Ahci::Protocol interface **
******************************/
void handle_irq() override
unsigned init(Port &port) override
{
Is::access_t status = Port::read<Is>();
port.write<Port::Cmd::Atapi>(1);
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>()));
}
retry<Port::Polling_timeout>(
[&] {
ack_irq();
_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();
if (state == TEST_READY && Port::Is::Dhrs::get(status)) {
Device_fis f(fis_base);
/* 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();
/* test unit ready */
_test_unit_ready(port);
port.wait_for(port.hba.delayer(), Port::Is::Dhrs::Equal(1));
port.ack_irq();
Device_fis f(port.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 (!f.read<Device_fis::Status::Device_ready>() || f.read<Device_fis::Error>())
throw Port::Polling_timeout();
if (state == READY && Port::Is::Dhrs::get(status)) {
ack_packets();
}
_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 (Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) {
switch (state) {
_block_count = host_to_big_endian(((unsigned *)port.device_info)[0]) + 1;
_block_size = host_to_big_endian(((unsigned *)port.device_info)[1]);
},
[&] {}, 3);
case STATUS:
test_unit_ready();
break;
case IDENTIFY:
state = READY;
state_change();
break;
case READY:
ack_packets();
return;
default:
break;
}
}
return 1;
}
Block::Session::Info info() const override
{
return { .block_size = block_size(),
.block_count = block_count(),
.align_log2 = 11,
return { .block_size = _block_size,
.block_count = _block_count,
.align_log2 = 1,
.writeable = false };
}
/*****************************
** Block::Driver interface **
*****************************/
bool dma_enabled() override { return true; };
Genode::size_t block_size() const override
void handle_irq(Port &port) override
{
return host_to_big_endian(((unsigned *)device_info)[1]);
port.ack_irq();
}
Block::sector_t block_count() const override
void writeable(bool) override { }
Response submit(Port &port, Block::Request const request) override
{
return host_to_big_endian(((unsigned *)device_info)[0]) + 1;
}
if (port.sanity_check(request) == false || port.dma_base == 0 ||
request.operation.type != Block::Operation::Type::READ)
return Response::REJECTED;
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();
if (_pending.operation.valid())
return Response::RETRY;
sanity_check(block_number, count);
pending = packet;
if (verbose)
Genode::log("add packet read ", block_number, " count ", count, " -> 0");
Block::Operation op = request.operation;
_pending = request;
_pending.success = false;
/* setup fis */
Command_table table(command_table_addr(0), phys, count * block_size());
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(block_number, count);
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(command_header_addr(0));
Command_header header(port.command_header_addr(0));
header.write<Command_header::Bits::W>(0);
header.clear_byte_count();
/* set pending */
execute(0);
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_ */

View File

@ -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; }
private:
Env &_env;
Dispatch &_dispatch;
struct Timer_delayer : Mmio::Delayer, Timer::Connection
{
Timer_delayer(Env &env)
: Timer::Connection(env) { }
void usleep(uint64_t us) override { Timer::Connection::usleep(us); }
} _delayer { _env };
Hba _hba { _env, _delayer };
Constructible<Ata::Protocol> _ata[MAX_PORTS];
Constructible<Atapi::Protocol> _atapi[MAX_PORTS];
Constructible<Port> _ports[MAX_PORTS];
Signal_handler<Driver> _irq { _env.ep(), *this, &Driver::handle_irq };
bool _enable_atapi;
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 _scan_ports(Region_map &rm)
{
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:
Driver(Env &env, Dispatch &dispatch, bool support_atapi)
: _env(env), _dispatch(dispatch), _enable_atapi(support_atapi)
{
_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);
}
};
class Block::Root_multiple_clients : public Root_component< ::Session_component>,
public Ahci_root
struct Ahci::Block_session_handler : Interface
{
private:
Env &env;
Port &port;
Ram_dataspace_capability ds;
Genode::Env &_env;
Genode::Allocator &_alloc;
Genode::Xml_node _config;
Signal_handler<Block_session_handler> request_handler
{ env.ep(), *this, &Block_session_handler::handle};
protected:
Block_session_handler(Env &env, Port &port, size_t buffer_size)
: env(env), port(port), ds(port.alloc_buffer(buffer_size))
{ }
::Session_component *_create_session(const char *args) override
~Block_session_handler()
{
Session_label const label = label_from_args(args);
Session_policy const policy(label, _config);
port.free_buffer(ds);
}
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);
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 {
driver.construct(env, *this, support_atapi);
report_ports();
} catch (Ahci::Missing_controller) {
error("no AHCI controller found");
env.parent().exit(~0);
} 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();
size_t session_size = sizeof(::Session_component)
+ sizeof(Factory) + tx_buf_size;
if (max((size_t)4096, session_size) > ram_quota) {
if (tx_buf_size > ram_quota.value) {
error("insufficient 'ram_quota' from '", label, "',"
" got ", ram_quota, ", need ", session_size);
" got ", ram_quota, ", need ", tx_buf_size);
throw Insufficient_ram_quota();
}
/* try read device port number attribute */
long num = policy.attribute_value("device", -1L);
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);
/* 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 _destroy_session(::Session_component *session) override
{
Driver_factory &factory = session->factory();
Genode::destroy(&_alloc, session);
Genode::destroy(&_alloc, &factory);
}
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
{
_env.parent().announce(_env.ep().manage(*this));
}
};
struct Block::Main
{
Genode::Env &env;
Genode::Heap heap { env.ram(), env.rm() };
Genode::Attached_rom_dataspace config { env, "config" };
Genode::Constructible<Genode::Reporter> reporter { };
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())
{
Genode::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");
env.parent().exit(~0);
} catch (Genode::Service_denied) {
Genode::error("hardware access denied");
env.parent().exit(~0);
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); }

View File

@ -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
{
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 { };
addr_t res_base { 0 };
size_t res_size { 0 };
X86_hba(Genode::Env &env) : env(env)
Ahci::Data::Data(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();
throw 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()), ")");
log("AHCI found ("
"vendor: ", Hex(pci_device->vendor_id()), " "
"device: ", Hex(pci_device->device_id()), " "
"class: ", 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();
/* 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());
uint16_t cmd = pci_device->config_read(PCI_CMD, Platform::Device::ACCESS_16BIT);
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);
_config_write(PCI_CMD, cmd, ::Platform::Device::ACCESS_16BIT);
irq.construct(pci_device->irq(0));
}
void disable_msi()
/************************
** Platform interface **
************************/
Genode::addr_t Ahci::Platform::_mmio_base() const
{
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);
/* iterate through cap pointers */
for (uint16_t val = 0; cap; cap = val >> 8) {
val = pci_device->config_read(cap, Platform::Device::ACCESS_16BIT);
if ((val & 0xff) != MSI_CAP)
continue;
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);
}
}
return addr_t(_data.iomem->local_addr<addr_t>());
}
void _config_write(uint8_t op, uint16_t cmd,
Platform::Device::Access_size width)
void Ahci::Platform::sigh_irq(Signal_context_capability sigh)
{
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);
_data.irq->sigh(sigh);
ack_irq();
}
void ack_irq() override { irq->ack_irq(); }
Ram_dataspace_capability
alloc_dma_buffer(Genode::size_t size) override
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 pci.alloc_dma_buffer(size); },
[&] () { pci.upgrade_caps(2); });
[&] () { return _data.pci.alloc_dma_buffer(size); },
[&] () { _data.pci.upgrade_caps(2); });
},
[&] () {
pci.upgrade_ram(donate);
_data.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 &)
void Ahci::Platform::free_dma_buffer(Genode::Ram_dataspace_capability ds)
{
static X86_hba h(env);
return h;
_data.pci.free_dma_buffer(ds);
}

View 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_ */

View File

@ -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

View File

@ -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

View 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_ */