genode/repos/os/src/drivers/ahci/ahci.h

949 lines
21 KiB
C
Raw Normal View History

/**
*
* \brief Generic AHCI controller definitions
* \author Sebasitan 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 _AHCI__AHCI_H_
#define _AHCI__AHCI_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 Ahci {
struct Missing_controller : Exception { };
class Platform;
struct Protocol;
struct Port;
struct Port_base;
struct Hba;
using Response = Block::Request_stream::Response;
using block_number_t = Block::block_number_t;
using block_count_t = Block::block_count_t;
}
class Ahci::Platform
{
private :
Data _data;
protected:
/**
* Return base address and size of HBA device registers
*/
addr_t _mmio_base() const;
public:
Platform(Env &env) : _data(env) { };
/**
* Register interrupt signal context
*/
void sigh_irq(Signal_context_capability sigh);
void ack_irq();
/**
* DMA
*/
Ram_dataspace_capability alloc_dma_buffer(size_t size);
void free_dma_buffer(Ram_dataspace_capability ds);
};
/**
* HBA definitions
*/
struct Ahci::Hba : Ahci::Platform,
Mmio
{
Mmio::Delayer &_delayer;
Hba(Env &env, Mmio::Delayer &delayer)
: Platform(env), Mmio(_mmio_base()), _delayer(delayer) { }
/**
* Host capabilites
*/
struct Cap : Register<0x0, 32>
{
struct Np : Bitfield<0, 4> { }; /* number of ports */
struct Ncs : Bitfield<8, 5> { }; /* number of command slots */
struct Iss : Bitfield<20, 4> { }; /* interface speed support */
struct Sncq : Bitfield<30, 1> { }; /* supports native command queuing */
struct Sa64 : Bitfield<31, 1> { }; /* supports 64 bit addressing */
};
unsigned port_count() { return read<Cap::Np>() + 1; }
unsigned command_slots() { return read<Cap::Ncs>() + 1; }
bool ncq() { return !!read<Cap::Sncq>(); }
bool supports_64bit(){ return !!read<Cap::Sa64>(); }
/**
* Generic host control
*/
struct Ghc : Register<0x4, 32>
{
struct Hr : Bitfield<0, 1> { }; /* hard reset */
struct Ie : Bitfield<1, 1> { }; /* interrupt enable */
struct Ae : Bitfield<31, 1> { }; /* AHCI enable */
};
/**
* Interrupt status
*/
struct Is : Register<0x8, 32> { };
void ack_irq()
{
write<Is>(read<Is>());
Platform::ack_irq();
}
/**
* Ports implemented
*/
struct Pi : Register<0xc, 32> { };
/**
* AHCI version
*/
struct Version : Register<0x10, 32>
{
struct Minor : Bitfield<0, 16> { };
struct Major : Bitfield<16, 16> { };
};
struct Cap2 : Register<0x24, 32> { };
void init()
{
/* enable AHCI */
write<Ghc::Ae>(1);
/* enable interrupts */
write<Ghc::Ie>(1);
}
Mmio::Delayer &delayer() { return _delayer; }
};
/***********************************
** AHCI commands and descriptors **
***********************************/
namespace Ahci {
struct Device_fis : Mmio
{
struct Status : Register<0x2, 8>
{
/* ATAPI bits */
struct Device_ready : Bitfield<6, 1> { };
};
struct Error : Register<0x3, 8> { };
Device_fis(addr_t recv_base)
: Mmio(recv_base + 0x40) { }
};
struct Command_fis : Mmio
{
struct Type : Register<0x0, 8> { }; /* FIS type */
struct Bits : Register<0x1, 8, 1>
{
struct C : Bitfield<7, 1> { }; /* update of command register */
};
struct Command : Register<0x2, 8, 1> { }; /* ATA command */
struct Features0_7 : Register<0x3, 8> { };
/* big-endian use byte access */
struct Lba0_7 : Register<0x4, 8> { };
struct Lba8_15 : Register<0x5, 8> { };
struct Lba16_23 : Register<0x6, 8> { };
struct Lba24 : Bitset_3<Lba0_7, Lba8_15, Lba16_23> { };
struct Device : Register<0x7, 8>
{
struct Lba : Bitfield<6, 1> { }; /* enable LBA mode */
};
/* big endian */
struct Lba24_31 : Register<0x8, 8> { };
struct Lba32_39 : Register<0x9, 8> { };
struct Lba40_47 : Register<0xa, 8> { };
struct Features8_15 : Register<0xb, 8> { };
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>
{
struct Tag : Bitfield<3, 5> { };
};
struct Sector8_15 : Register<0xd, 8> { };
struct Sector : Bitset_2<Sector0_7, Sector8_15> { }; /* sector count */
Command_fis(addr_t base)
: Mmio(base)
{
clear();
enum { HOST_TO_DEVICE = 0x27 };
write<Type>(HOST_TO_DEVICE);
}
static constexpr size_t size() { return 0x14; }
void clear() { memset((void *)base(), 0, size()); }
/************************
** ATA spec commands **
************************/
void identify_device()
{
write<Bits::C>(1);
write<Device::Lba>(0);
write<Command>(0xec);
}
void dma_ext(bool read, block_number_t block_number, block_count_t block_count)
{
write<Bits::C>(1);
write<Device::Lba>(1);
/* read_dma_ext : write_dma_ext */
write<Command>(read ? 0x25 : 0x35);
write<Lba>(block_number);
write<Sector>(block_count);
}
void fpdma(bool read, block_number_t block_number, block_count_t block_count,
unsigned slot)
{
write<Bits::C>(1);
write<Device::Lba>(1);
/* read_fpdma : write_fpdma */
write<Command>(read ? 0x60 : 0x61);
write<Lba>(block_number);
write<Features>(block_count);
write<Sector0_7::Tag>(slot);
}
void atapi()
{
write<Bits::C>(1);
/* 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 : Mmio
{
struct Bits : Register<0x0, 16>
{
struct Cfl : Bitfield<0, 5> { }; /* command FIS length */
struct A : Bitfield<5, 1> { }; /* ATAPI command flag */
struct W : Bitfield<6, 1> { }; /* write flag */
struct P : Bitfield<7, 1> { }; /* prefetchable flag */
struct C : Bitfield<10, 1> { }; /* clear busy upon R_OK */
};
struct Prdtl : Register<0x2, 16> { }; /* physical region descr. length */
struct Prdbc : Register<0x4, 32> { }; /* PRD byte count */
struct Ctba0 : Register<0x8, 32> { }; /* command table base addr (low) */
struct Ctba0_u0 : Register<0xc, 32> { }; /* command table base addr (upper) */
Command_header(addr_t base) : Mmio(base) { }
void cmd_table_base(addr_t base_phys)
{
uint64_t addr = base_phys;
write<Ctba0>(addr);
write<Ctba0_u0>(addr >> 32);
write<Prdtl>(1);
write<Bits::Cfl>(Command_fis::size() / sizeof(unsigned));
}
void clear_byte_count()
{
write<Prdbc>(0);
}
void atapi_command()
{
write<Bits::A>(1);
}
static constexpr size_t size() { return 0x20; }
};
/**
* ATAPI packet 12 or 16 bytes
*/
struct Atapi_command : Mmio
{
struct Command : Register<0, 8> { };
/* LBA32 big endian */
struct Lba24_31 : Register<0x2, 8> { };
struct Lba16_23 : Register<0x3, 8> { };
struct Lba8_15 : Register<0x4, 8> { };
struct Lba0_7 : Register<0x5, 8> { };
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 : Bitset_2<Sector0_7, Sector8_15> { };
Atapi_command(addr_t base) : Mmio(base)
{
memset((void *)base, 0, 16);
}
void read_capacity()
{
write<Command>(0x25);
}
void test_unit_ready()
{
write<Command>(0x0);
}
void read_sense()
{
write<Command>(0x3);
write<Lba8_15>(18);
}
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 : Mmio
{
struct Dba : Register<0x0, 32> { }; /* data base address */
struct Dbau : Register<0x4, 32> { }; /* data base address upper 32 bits */
struct Bits : Register<0xc, 32>
{
struct Dbc : Bitfield<0, 22> { }; /* data byte count */
struct Irq : Bitfield<31,1> { }; /* interrupt completion */
};
Prdt(addr_t base, addr_t phys, size_t bytes)
: Mmio(base)
{
uint64_t addr = phys;
write<Dba>(addr);
write<Dbau>(addr >> 32);
write<Bits::Dbc>(bytes > 0 ? bytes - 1 : 0);
}
static constexpr size_t size() { return 0x10; }
};
struct Command_table
{
Command_fis fis;
Atapi_command atapi_cmd;
/* in Genode we only need one PRD (for one packet) */
Prdt prdt;
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 size_t size() { return 0x100; }
};
} /* namespace Ahci */
/**
* Minimalistic AHCI port structure to merely detect device signature
*/
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 addr_t offset() { return 0x100; }
static constexpr size_t size() { return 0x80; }
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 Ahci::Port : private Port_base
{
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
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 : Exception { };
Protocol &protocol;
Region_map &rm;
unsigned cmd_slots = hba.command_slots();
Ram_dataspace_capability device_ds { };
Ram_dataspace_capability cmd_ds { };
Ram_dataspace_capability device_info_ds { };
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 */
Port(Protocol &protocol, Region_map &rm, Hba &hba,
unsigned index)
:
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);
hba.free_dma_buffer(device_ds);
}
if (cmd_ds.valid()) {
rm.detach((void *)cmd_table);
hba.free_dma_buffer(cmd_ds);
}
if (device_info_ds.valid()) {
rm.detach((void*)device_info);
hba.free_dma_buffer(device_info_ds);
}
}
/**
* Command list base (1K length naturally aligned)
*/
struct Clb : Register<0x0, 32> { };
/**
* Command list base upper 32 bit
*/
struct Clbu : Register<0x4, 32> { };
void command_list_base(addr_t phys)
{
uint64_t addr = phys;
write<Clb>(addr);
write<Clbu>(addr >> 32);
}
/**
* FIS base address (256 bytes naturally aligned)
*/
struct Fb : Register<0x8, 32> { };
/**
* FIS base address upper 32 bit
*/
struct Fbu : Register<0xc, 32> { };
void fis_rcv_base(addr_t phys)
{
uint64_t addr = phys;
write<Fb>(addr);
write<Fbu>(addr >> 32);
}
/**
* Port interrupt status
*/
struct Is : Register<0x10, 32, 1>
{
struct Dhrs : Bitfield<0, 1> { }; /* device to host register FIS */
struct Pss : Bitfield<1, 1> { }; /* PIO setup FIS */
struct Dss : Bitfield<2, 1> { }; /* DMA setup FIS */
struct Sdbs : Bitfield<3, 1> { }; /* Set device bit */
struct Pcs : Bitfield<6, 1> { }; /* port connect change status */
struct Prcs : Bitfield<22, 1> { }; /* PhyRdy change status */
struct Infs : Bitfield<26, 1> { }; /* interface non-fatal error */
struct Ifs : Bitfield<27, 1> { }; /* interface fatal error */
/* ncq irq */
struct Fpdma_irq : Sdbs { };
/* non-ncq irq */
struct Dma_ext_irq : Bitfield<0, 3> { };
};
void ack_irq()
{
Is::access_t status = read <Is>();
/* clear Serr.Diag.x */
if (Is::Pcs::get(status))
write<Serr::Diag::X>(0);
/* clear Serr.Diag.n */
if (Is::Prcs::get(status))
write<Serr::Diag::N>(0);
write<Is>(read<Is>());
};
/**
* Port interrupt enable
*/
struct Ie : Register<0x14, 32, 1>
{
struct Dhre : Bitfield<0, 1> { }; /* device to host register FIS interrupt */
struct Pse : Bitfield<1, 1> { }; /* PIO setup FIS interrupt */
struct Dse : Bitfield<2, 1> { }; /* DMA setup FIS interrupt */
struct Sdbe : Bitfield<3, 1> { }; /* set device bits FIS interrupt (ncq) */
struct Ufe : Bitfield<4, 1> { }; /* unknown FIS */
struct Dpe : Bitfield<5, 1> { }; /* descriptor processed */
struct Ifne : Bitfield<26, 1> { }; /* interface non-fatal error */
struct Ife : Bitfield<27, 1> { }; /* interface fatal error */
struct Hbde : Bitfield<28, 1> { }; /* host bus data error */
struct Hbfe : Bitfield<29, 1> { }; /* host bus fatal error */
struct Tfee : Bitfield<30, 1> { }; /* task file error */
};
void interrupt_enable()
{
Ie::access_t ie = 0;
Ie::Dhre::set(ie, 1);
Ie::Pse::set(ie, 1);
Ie::Dse::set(ie, 1);
Ie::Sdbe::set(ie, 1);
Ie::Ufe::set(ie, 1);
Ie::Dpe::set(ie, 1);
Ie::Ifne::set(ie, 1);
Ie::Hbde::set(ie, 1);
Ie::Hbfe::set(ie, 1);
Ie::Tfee::set(ie, 1);
write<Ie>(~0U);
}
/**
* Port command
*/
struct Cmd : Register<0x18, 32>
{
struct St : Bitfield<0, 1> { }; /* start */
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 */
};
void start()
{
if (read<Cmd::St>())
return;
try {
wait_for(hba.delayer(), Tfd::Sts_bsy::Equal(0));
} catch (Polling_timeout) {
error("HBA busy unable to start command processing.");
return;
}
try {
wait_for(hba.delayer(), Tfd::Sts_drq::Equal(0));
} catch (Polling_timeout) {
error("HBA in DRQ unable to start command processing.");
return;
}
write<Cmd::St>(1);
}
void stop()
{
if (!(read<Ci>() | read<Sact>()))
write<Cmd::St>(0);
}
void power_up()
{
Cmd::access_t cmd = read<Cmd>();
Cmd::Sud::set(cmd, 1);
Cmd::Pod::set(cmd, 1);
Cmd::Fre::set(cmd, 1);
write<Cmd>(cmd);
}
/**
* Task file data
*/
struct Tfd : Register<0x20, 32>
{
struct Sts_drq : Bitfield<3, 1> { }; /* indicates data transfer request */
struct Sts_bsy : Bitfield<7, 1> { }; /* interface is busy */
};
/**
* Serial ATA status
*/
struct Ssts : Register<0x28, 32>
{
struct Dec : Bitfield<0, 4> /* device detection */
{
enum {
NONE = 0x0,
ESTABLISHED = 0x3
};
};
struct Ipm : Bitfield<8, 4> /* interface power mgmt */
{
enum {
ACTIVE = 0x1,
SUSPEND = 0x2,
};
};
};
bool enable()
{
Ssts::access_t status = read<Ssts>();
if (Ssts::Dec::get(status) == Ssts::Dec::NONE)
return false;
/* if in power-mgmt state (partial or slumber) */
if (Ssts::Ipm::get(status) & Ssts::Ipm::SUSPEND) {
/* try to wake up device */
write<Cmd::Icc>(Ssts::Ipm::ACTIVE);
retry<Not_ready>(
[&] {
if ((Ssts::Dec::get(status) != Ssts::Dec::ESTABLISHED) ||
!(Ssts::Ipm::get(status) & Ssts::Ipm::ACTIVE))
throw Not_ready();
},
[&] {
hba.delayer().usleep(1000);
status = read<Ssts>();
}, 10);
}
return ((Ssts::Dec::get(status) == Ssts::Dec::ESTABLISHED) &&
(Ssts::Ipm::get(status) & Ssts::Ipm::ACTIVE));
}
/**
* Serial ATA control
*/
struct Sctl : Register<0x2c, 32>
{
struct Det : Bitfield<0, 4> { }; /* device dectection initialization */
struct Ipmt : Bitfield<8, 4> { }; /* allowed power management transitions */
};
void reset()
{
if (read<Cmd::St>())
warning("CMD.ST bit set during device reset --> unknown behavior");
write<Sctl::Det>(1);
hba.delayer().usleep(1000);
write<Sctl::Det>(0);
try {
wait_for(hba.delayer(), Ssts::Dec::Equal(Ssts::Dec::ESTABLISHED));
} catch (Polling_timeout) {
warning("Port reset failed");
}
}
/**
* Serial ATA error
*/
struct Serr : Register<0x30, 32>
{
struct Diag : Register<0x2, 16>
{
struct N : Bitfield<0, 1> { }; /* PhyRdy change */
struct X : Bitfield<10, 1> { }; /* exchanged */
};
};
void clear_serr() { write<Serr>(read<Serr>()); }
/**
* Serial ATA active (strict write)
*/
struct Sact : Register<0x34, 32, true> { };
/**
* Command issue (strict write)
*/
struct Ci : Register<0x38, 32, true> { };
void init()
{
/* stop command list processing */
stop();
/* setup command list/table */
setup_memory();
/* disallow all power-management transitions */
write<Sctl::Ipmt>(0x3);
/* power-up device */
power_up();
/* reset port */
reset();
/* clean error register */
clear_serr();
/* enable required interrupts */
interrupt_enable();
/* acknowledge all pending interrupts */
ack_irq();
hba.ack_irq();
}
void setup_memory()
{
device_ds = hba.alloc_dma_buffer(0x1000);
/* command list 1K */
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 = 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)Dataspace_client(cmd_ds).phys_addr();
/* set command table addresses in command list */
for (unsigned i = 0; i < cmd_slots; i++) {
Command_header h(cmd_list + (i * Command_header::size()));
h.cmd_table_base(phys + (i * Command_table::size()));
}
/* dataspace for device info */
device_info_ds = hba.alloc_dma_buffer(0x1000);
device_info = rm.attach(device_info_ds);
}
addr_t command_table_addr(unsigned slot)
{
return cmd_table + (slot * Command_table::size());
};
addr_t command_header_addr(unsigned slot)
{
return cmd_list + (slot * Command_header::size());
}
void execute(unsigned slot)
{
start();
write<Ci>(1U << slot);
}
bool sanity_check(Block::Request const &request)
{
Block::Session::Info const i = info();
/* max. PRDT size is 4MB */
if (request.operation.count * i.block_size > 4 * 1024 * 1024) {
error("error: maximum supported packet size is 4MB");
return false;
}
/* sanity check */
if (request.operation.count + request.operation.block_number > i.block_count) {
error("error: requested blocks are outside of device");
return false;
}
return true;
}
Ram_dataspace_capability alloc_buffer(size_t 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_buffer(Ram_dataspace_capability ds)
{
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 /* _AHCI__AHCI_H_ */