ahci: new AHCI driver implementation

Supports native command queueing and multiple ports.
This commit is contained in:
Sebastian Sumpf 2015-03-25 11:12:52 +01:00 committed by Christian Helmuth
parent 33bc63e7c3
commit 7910b5146f
30 changed files with 2248 additions and 3305 deletions

View File

@ -1,4 +1,4 @@
set mkfs_cmd mkfs.vfat
set mkfs_cmd [check_installed mkfs.vfat]
set mkfs_opts "-F32"
set filesystem ffat

View File

@ -6,9 +6,7 @@
set use_sd_card_drv [expr [have_spec omap4] || [have_spec platform_arndale] || [have_spec pl180]]
set use_ahci_drv [have_spec x86]
if {[catch { exec which mkfs.vfat } ]} {
puts stderr "Error: mkfs.vfat not installed, aborting test"; exit }
set mkfs [check_installed mkfs.vfat]
if {[expr [have_spec linux] || [have_spec hw_odroid_xu]]} {
puts "Run script does not support this platform"; exit }
@ -85,22 +83,30 @@ append_if [have_spec acpi] config {
<service name="IRQ" />
</provides>
<route>
<service name="PCI"> <any-child /> </service>
<any-service> <parent/> <any-child /> </any-service>
</route>
<config>
<policy label="acpi_drv"><pci class="ALL" /></policy>
<policy label="ahci_drv"><pci class="AHCI" /></policy>
</config>
</start>}
append_if [expr ![have_spec acpi] && [have_spec pci]] config {
<start name="pci_drv">
<resource name="RAM" quantum="5M" constrain_phys="yes"/>
<provides><service name="PCI"/></provides>
<config>
<policy label="ahci_drv"/><pci class="AHCI" /></policy>
</config>
</start>}
append_if $use_ahci_drv config {
<start name="ahci">
<start name="ahci_drv">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Block"/> </provides>
<config ata="yes" />
<config>
<policy label="ffat_fs" device="0" />
</config>
</start>
}
@ -130,7 +136,7 @@ set boot_modules {
lappend_if [have_spec pci] boot_modules pci_drv
lappend_if [have_spec acpi] boot_modules acpi_drv
lappend_if $use_ahci_drv boot_modules ahci
lappend_if $use_ahci_drv boot_modules ahci_drv
lappend_if $use_sd_card_drv boot_modules sd_card_drv
build_boot_image $boot_modules
@ -144,7 +150,7 @@ set cmd "dd if=/dev/zero of=$disk_image bs=1024 count=65536"
puts "creating disk image: $cmd"
catch { exec sh -c $cmd }
set cmd "mkfs.vfat -F32 $disk_image"
set cmd "$mkfs -F32 $disk_image"
puts "formating disk image with vfat file system: $cmd"
catch { exec sh -c $cmd }

View File

@ -126,16 +126,17 @@ append_if [expr ![have_spec acpi] && [have_spec pci]] config {
append_if $use_ahci config {
<start name="ahci">
<binary name="ahci" />
<binary name="ahci_drv" />
<resource name="RAM" quantum="10M" />
<provides><service name="Block" /></provides>
<route>}
append_if [expr $use_ahci && [have_spec acpi]] config {
<service name="IRQ"><child name="acpi" /></service>}
append_if $use_ahci config {
<route>
<any-service> <parent /> <any-child /></any-service>
</route>
<config>}
append_if $use_ahci config "
<policy label=\"test-libc_$filesystem\" device=\"0\" />"
append_if $use_ahci config {
</config>
</start>}
append_if $use_sd_card_drv config {
@ -167,7 +168,7 @@ append boot_modules libc_$filesystem.lib.so
lappend_if [have_spec pci] boot_modules pci_drv
lappend_if [have_spec acpi] boot_modules acpi_drv
lappend_if $use_ahci boot_modules ahci
lappend_if $use_ahci boot_modules ahci_drv
lappend_if $use_sd_card_drv boot_modules sd_card_drv
build_boot_image $boot_modules

View File

@ -129,6 +129,25 @@ proc drivers_start_nodes { feature_arg } {
<service name="PCI"/>
<service name="IRQ" />
</provides>
<binary name="acpi_drv"/>
<provides>
<service name="PCI"/>
<service name="IRQ" />
</provides>
<config>
<policy label="acpi_drv">
<pci class="ALL"/>
</policy>
<policy label="ahci">
<pci class="AHCI"/>
</policy>
<policy label="ps2_drv">
<device name="PS2" />
</policy>
<policy label="fb_drv">
<pci class="VGA" />
</policy>
</config>
<route>
<service name="PCI"> <any-child /> </service>
<any-service> <parent/> <any-child /> </any-service>
@ -179,6 +198,17 @@ proc drivers_start_nodes { feature_arg } {
<start name="pci_drv">
<resource name="RAM" quantum="2M" constrain_phys="yes"/>
<provides><service name="PCI"/></provides>
<config>
<policy label="ahci">
<pci class="AHCI"/>
</policy>
<policy label="ps2_drv">
<device name="PS2" />
</policy>
<policy label="fb_drv">
<pci class="VGA" />
</policy>
</config>
</start>
}

View File

@ -51,9 +51,12 @@ append config [qt5_start_nodes feature]
append_if $use_ahci_driver config {
<start name="ahci">
<resource name="RAM" quantum="1M"/>
<binary name="ahci_drv" />
<resource name="RAM" quantum="5M"/>
<provides> <service name="Block"/> </provides>
<config ata="yes" />
<config>
<policy label="ffat_fs" device="0" />
</config>
</start>}
append_if $use_sd_card_driver config {
@ -145,7 +148,7 @@ append boot_modules {
lappend_if [have_spec linux] boot_modules ram_fs
lappend_if [expr ![have_spec linux]] boot_modules ffat_fs
lappend_if $use_sd_card_driver boot_modules sd_card_drv
lappend_if $use_ahci_driver boot_modules ahci
lappend_if $use_ahci_driver boot_modules ahci_drv
build_boot_image $boot_modules

View File

@ -1,17 +0,0 @@
#
# \brief Generic toolchain configurations for AHCI
# \author Martin Stein <martin.stein@genode-labs.com>
# \date 2013-05-17
#
# add C++ sources
SRC_CC += main.cc
# add library dependencies
LIBS += base
# add include directories
INC_DIR += $(REP_DIR)/src/drivers/ahci/include
# declare source paths
vpath main.cc $(REP_DIR)/src/drivers/ahci

View File

@ -1,17 +0,0 @@
#
# \brief Toolchain configurations for AHCI on Exynos
# \author Martin Stein <martin.stein@genode-labs.com>
# \date 2013-05-17
#
# add C++ sources
SRC_CC += ahci_driver.cc
# add include directories
INC_DIR += $(REP_DIR)/src/drivers/ahci/exynos5
# declare source paths
vpath ahci_driver.cc $(REP_DIR)/src/drivers/ahci/exynos5
# insert configurations that are less specific
include $(REP_DIR)/lib/mk/ahci.inc

View File

@ -0,0 +1,6 @@
SRC_CC = platform.cc
INC_DIR += $(REP_DIR)/src/drivers/ahci
vpath platform.cc $(REP_DIR)/src/drivers/ahci/exynos5

View File

@ -1,11 +0,0 @@
#
# \brief Toolchain configurations for AHCI on X86
# \author Martin Stein <martin.stein@genode-labs.com>
# \date 2013-05-17
#
# add include directories
INC_DIR += $(REP_DIR)/src/drivers/ahci/x86
# include less specific config
include $(REP_DIR)/lib/mk/ahci.inc

View File

@ -0,0 +1,6 @@
SRC_CC = platform.cc
INC_DIR += $(REP_DIR)/src/drivers/ahci
vpath platform.cc $(REP_DIR)/src/drivers/ahci/x86

View File

@ -1,19 +1,27 @@
This directory contains an implementation of a simple AHCI driver.
This directory contains the implementation of Genode's AHCI driver
Behavior
--------
The server implements Genode's new block-driver API ('os/include/block'), thus
exposing the block-session interface as front-end. AHCI depends on Genode's PCI
driver as well as the timer server. For a usage example see: 'os/run/ahci.run'.
The driver supports x86 32/64 bit platforms and the Exynos5 SOC. If
more than one AHCI controller is present, the first one will be used.
Each active device on each AHCI port will be represented by a Genode
block session. The server must be configured via a policy, that states
which client can access a certain device:
Limitations and known issues
----------------------------
Currently, the server scans the PCI bus at startup and retrieves the first available
AHCI controller, scans the controller ports and uses the first non-ATAPI port
where a device is present.
!<start name="ahci">
! <binary name="ahci_drv" />
! <resource name="RAM" quantum="10M" />
! <provides><service name="Block" /></provides> }
! <route>
! <any-service> <parent /> <any-child /> </any-service>
! </route>
! <config>
! <policy label="test-ahci" device="0" />
! <policy label="bench" device="1" />
! </config>
!</start>
On real hardware and on kernels taking advantage of I/O APICs (namely NOVA and
Fiasco.OC) we still lack support for ACPI parsing and thus for interrupts,
leading to a non-working driver.
In the example above, a session request labeled with "test-ahci"
gains access to device 0, while "bench" gains access to device 1.

View File

@ -0,0 +1,208 @@
/**
* \brief AHCI port controller handling
* \author Sebastian Sumpf
* \date 2015-04-29
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <timer_session/connection.h>
#include "ata_driver.h"
#include "atapi_driver.h"
struct Timer_delayer : Mmio::Delayer, Timer::Connection
{
void usleep(unsigned us) { Timer::Connection::usleep(us); }
};
Mmio::Delayer &Hba::delayer()
{
static Timer_delayer d;
return d;
}
struct Ahci
{
/* read device signature */
enum Signature {
ATA_SIG = 0x101,
ATAPI_SIG = 0xeb140101,
ATAPI_SIG_QEMU = 0xeb140000, /* will be fixed in Qemu */
};
Ahci_root &root;
Platform::Hba &platform_hba = Platform::init(Hba::delayer());
Hba hba = { platform_hba };
enum { MAX_PORTS = 32 };
Port_driver *ports[MAX_PORTS];
bool port_claimed[MAX_PORTS];
Signal_rpc_member<Ahci> irq;
Signal_rpc_member<Ahci> device_ready;
unsigned ready_count = 0;
Ahci(Ahci_root &root)
: root(root), irq(root.entrypoint(), *this, &Ahci::handle_irq),
device_ready(root.entrypoint(), *this, &Ahci::ready)
{
info();
/* register irq handler */
platform_hba.sigh_irq(irq);
/* initialize HBA (IRQs, memory) */
hba.init();
/* search for devices */
scan_ports();
}
bool is_atapi(unsigned sig)
{
return sig == ATAPI_SIG_QEMU || sig == ATAPI_SIG;
}
/**
* Forward IRQs to ports
*/
void handle_irq(unsigned)
{
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();
}
void ready(unsigned)
{
if (--ready_count)
return;
/* announce service */
root.announce();
}
void info()
{
PINF("\tversion: %x.%04x", hba.read<Hba::Version::Major>(),
hba.read<Hba::Version::Minor>());
PINF("\tcommand slots: %u", hba.command_slots());
PINF("\tnative command queuing: %s", hba.ncg() ? "yes" : "no");
PINF("\t64 bit support: %s", hba.supports_64bit() ? "yes" : "no");
}
void scan_ports()
{
PINF("\tnumber of ports: %u pi: %x", hba.port_count(), hba.read<Hba::Pi>());
unsigned avaiable = hba.read<Hba::Pi>();
for (unsigned i = 0; i < hba.port_count(); i++) {
/* check if port is implemented */
if (!(avaiable & (1U << i)))
continue;
Port port(hba, platform_hba, i);
bool enabled = port.enable();
unsigned sig = port.read<Port::Sig>();
PINF("\t\t#%u: %s", i, enabled ?
(is_atapi(sig) ? "ATAPI" : "ATA") :
"off");
if (!enabled)
continue;
switch (sig) {
case ATA_SIG:
ports[i] = new (Genode::env()->heap()) Ata_driver(port, device_ready);
ready_count++;
break;
case ATAPI_SIG:
case ATAPI_SIG_QEMU:
ports[i] = new (Genode::env()->heap()) Atapi_driver(port, device_ready);
ready_count++;
break;
default:
PWRN("Device signature %x unsupported", sig);
}
}
};
Block::Driver *claim_port(unsigned port_num)
{
if (!is_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 is_avail(unsigned port_num)
{
return port_num < MAX_PORTS && ports[port_num] && !port_claimed[port_num] &&
ports[port_num]->ready();
}
};
static Ahci *sata_ahci(Ahci *ahci = 0)
{
static Ahci *a = ahci;
return a;
}
void Ahci_driver::init(Ahci_root &root)
{
static Ahci ahci(root);
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::is_avail(long device_num)
{
return sata_ahci()->is_avail(device_num);
}

View File

@ -0,0 +1,866 @@
/**
* \brief Generic AHCI controller definitions
* \author Sebasitan Sumpf
* \date 2015-04-29
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__AHCI_H_
#define _INCLUDE__AHCI_H_
#include <block/component.h>
#include <os/attached_mmio.h>
#include <util/volatile_object.h>
static bool constexpr verbose = false;
namespace Platform {
struct Hba;
Hba &init(Genode::Mmio::Delayer &delayer);
};
struct Ahci_root
{
virtual Server::Entrypoint &entrypoint() = 0;
virtual void announce() = 0;
};
namespace Ahci_driver {
void init(Ahci_root &ep);
bool is_avail(long device_num);
Block::Driver *claim_port(long device_num);
void free_port(long device_num);
}
struct Platform::Hba
{
/**
* Return base address and size of HBA device registers
*/
virtual Genode::addr_t base() const = 0;
virtual Genode::size_t size() const = 0;
/**
* Register interrupt signal context
*/
virtual void sigh_irq(Genode::Signal_context_capability sigh) = 0;
virtual void ack_irq() = 0;
/**
* 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;
};
/**
* HBA definitions
*/
struct Hba : Genode::Attached_mmio
{
Hba(Platform::Hba &hba)
: Attached_mmio(hba.base(), hba.size()) { }
/**
* 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 Sncg : 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 ncg() { return !!read<Cap::Sncg>(); }
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>()); }
/**
* 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);
}
static Mmio::Delayer &delayer();
};
struct Device_fis : Genode::Mmio
{
struct Status : Register<0x2, 8>
{
/* ATAPI bits */
struct Device_ready : Bitfield<6, 1> { };
};
struct Error : Register<0x3, 8> { };
Device_fis(Genode::addr_t recv_base)
: Mmio(recv_base + 0x40) { }
};
struct Command_fis : Genode::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 : Genode::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 : 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 */
/* big endian */
struct Sector0_7 : Register<0xc, 8, 1>
{
struct Tag : Bitfield<3, 5> { };
};
struct Sector8_15 : Register<0xd, 8> { };
struct Sector : Genode::Bitset_2<Sector0_7, Sector8_15> { }; /* sector count */
Command_fis(Genode::addr_t base)
: Mmio(base)
{
clear();
enum { HOST_TO_DEVICE = 0x27 };
write<Type>(HOST_TO_DEVICE);
}
static constexpr Genode::size_t size() { return 0x14; }
void clear() { Genode::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::sector_t block_number, Genode::size_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::sector_t block_number, Genode::size_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);
}
};
/**
* AHCI command list structure header
*/
struct Command_header : Genode::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(Genode::addr_t base) : Mmio(base) { }
void cmd_table_base(Genode::addr_t base_phys)
{
Genode::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 Genode::size_t size() { return 0x20; }
};
/**
* ATAPI packet 12 or 16 bytes
*/
struct Atapi_command : Genode::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 : Genode::Bitset_3<Lba0_7, Lba8_15, Lba16_23> { };
struct Lba : Genode::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> { };
Atapi_command(Genode::addr_t base) : Mmio(base)
{
Genode::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::sector_t block_number, Genode::size_t block_count)
{
write<Command>(0x28);
write<Lba>(block_number);
write<Sector>(block_count);
}
};
/**
* Physical region descritpor table
*/
struct Prdt : Genode::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(Genode::addr_t base, Genode::addr_t phys, Genode::size_t bytes)
: Mmio(base)
{
Genode::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; }
};
struct Command_table
{
Command_fis fis;
Atapi_command atapi_cmd;
/* 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)
: fis(base), atapi_cmd(base + 0x40),
prdt(base + 0x80, phys, bytes)
{ }
static constexpr Genode::size_t size() { return 0x100; }
};
struct Identity : Genode::Mmio
{
Identity(Genode::addr_t base) : Mmio(base) { }
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()
{
PLOG("\t\tqueue depth: %u ncg: %u",
read<Queue_depth::Max_depth>() + 1,
read<Sata_caps::Ncq_support>());
PLOG("\t\tnumer of sectors: %llu", read<Sector_count>());
PLOG("\t\tmultiple logical blocks per physical: %s",
read<Logical_block::Multiple>() ? "yes" : "no");
PLOG("\t\tlogical blocks per physical: %u",
1U << read<Logical_block::Per_physical>());
PLOG("\t\tlogical block size is above 512 byte: %s",
read<Logical_block::Longer_512>() ? "yes" : "no");
PLOG("\t\twords (16bit) per logical block: %u",
read<Logical_words>());
PLOG("\t\toffset of first logical block within physical: %u",
read<Alignment::Logical_offset>());
}
};
/**
* AHCI port
*/
struct Port : Genode::Mmio
{
Hba &hba;
Platform::Hba &platform_hba;
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;
Genode::addr_t cmd_list = 0;
Genode::addr_t fis_base = 0;
Genode::addr_t cmd_table = 0;
Genode::addr_t device_info = 0;
enum State {
NONE,
STATUS,
TEST_READY,
IDENTIFY,
READY,
};
State state = NONE;
Port(Hba &hba, Platform::Hba &platform_hba, unsigned number)
: Mmio(hba.base + offset() + (number * size())), hba(hba),
platform_hba(platform_hba)
{
stop();
if (!wait_for<Cmd::Cr>(0, Hba::delayer(), 500, 1000))
PERR("failed to stop command list processing");
}
virtual ~Port()
{
if (device_ds.valid()) {
Genode::env()->rm_session()->detach((void *)cmd_list);
platform_hba.free_dma_buffer(device_ds);
}
if (cmd_ds.valid()) {
Genode::env()->rm_session()->detach((void *)cmd_table);
platform_hba.free_dma_buffer(cmd_ds);
}
if (device_info_ds.valid()) {
Genode::env()->rm_session()->detach((void*)device_info);
platform_hba.free_dma_buffer(device_info_ds);
}
}
static constexpr Genode::addr_t offset() { return 0x100; }
static constexpr Genode::size_t size() { return 0x80; }
/**
* 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(Genode::addr_t phys)
{
Genode::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(Genode::addr_t phys)
{
Genode::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 */
};
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, 2> { }; /* PIO setup FIS interrupt */
struct Dse : Bitfield<2, 1> { }; /* DMA setup FIS interrupt */
struct Sdbe : Bitfield<3, 1> { }; /* set device bits FIS interrupt (ncg) */
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 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;
if (!wait_for<Tfd::Sts_bsy>(0, hba.delayer(), 500, 1000)) {
PERR("HBA busy unable to start command processing.");
return;
}
if (!wait_for<Tfd::Sts_drq>(0, hba.delayer(), 500, 1000)) {
PERR("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 */
};
/**
* Port signature
*/
struct Sig : Register<0x24, 32> { };
/**
* 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);
while ((Ssts::Dec::get(status) != Ssts::Dec::ESTABLISHED) ||
!(Ssts::Ipm::get(status) & Ssts::Ipm::ACTIVE))
status = read<Ssts>();
}
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>())
PWRN("CMD.ST bit set during device reset --> unknown behavior");
write<Sctl::Det>(1);
hba.delayer().usleep(1000);
write<Sctl::Det>(0);
if (!wait_for<Ssts::Dec>(Ssts::Dec::ESTABLISHED, hba.delayer()))
PWRN("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
*/
struct Sact : Register<0x34, 32> { };
/**
* Command issue
*/
struct Ci : Register<0x38, 32> { };
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()
{
using Genode::addr_t;
using Genode::size_t;
device_ds = platform_hba.alloc_dma_buffer(0x1000);
/* command list 1K */
addr_t phys = Genode::Dataspace_client(device_ds).phys_addr();
cmd_list = (addr_t)Genode::env()->rm_session()->attach(device_ds);
command_list_base(phys);
/* receive FIS base 256 byte */
fis_base = cmd_list + 1024;
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);
cmd_table = (addr_t)Genode::env()->rm_session()->attach(cmd_ds);
phys = (addr_t)Genode::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 = platform_hba.alloc_dma_buffer(0x1000);
device_info = Genode::env()->rm_session()->attach(device_info_ds);
}
Genode::addr_t command_table_addr(unsigned slot)
{
return cmd_table + (slot * Command_table::size());
};
Genode::addr_t command_header_addr(unsigned slot)
{
return cmd_list + (slot * Command_header::size());
}
void execute(unsigned slot)
{
start();
write<Ci>(1U << slot);
}
bool ready() { return state == READY; }
};
struct Port_driver : Port, Block::Driver
{
Genode::Signal_context_capability state_change_cap;
Port_driver(Port &port, Genode::Signal_context_capability state_change_cap)
: Port(port), state_change_cap(state_change_cap)
{ }
virtual void handle_irq() = 0;
void state_change() { Genode::Signal_transmitter(state_change_cap).submit(); }
void sanity_check(Block::sector_t block_number, Genode::size_t count)
{
/* max. PRDT size is 4MB */
if (count * block_size() > 4 * 1024 * 1024) {
PERR("error: maximum supported packet size is 4MB");
throw Io_error();
}
/* sanity check */
if (block_number + count > block_count()) {
PERR("error: requested blocks are outside of device");
throw Io_error();
}
}
/*******************
** Block::Driver **
*******************/
Genode::Ram_dataspace_capability
alloc_dma_buffer(Genode::size_t size) override
{
return platform_hba.alloc_dma_buffer(size);
}
void free_dma_buffer(Genode::Ram_dataspace_capability c) override
{
platform_hba.free_dma_buffer(c);
}
};
#endif /* _INCLUDE__AHCI_H_ */

View File

@ -0,0 +1,212 @@
/**
* \brief AHCI-port driver for ATA devices
* \author Sebastian Sumpf
* \date 2015-04-29
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _ATA_DRIVER_H_
#define _ATA_DRIVER_H_
#include "ahci.h"
using namespace Genode;
struct Ata_driver : Port_driver
{
Genode::Lazy_volatile_object<Identity> info;
Block::Packet_descriptor pending[32];
Ata_driver(Port &port, Signal_context_capability state_change)
: Port_driver(port, state_change)
{
Port::init();
identify_device();
}
unsigned find_free_cmd_slot()
{
for (unsigned slot = 0; slot < cmd_slots; slot++)
if (!pending[slot].valid())
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].valid())
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].valid())
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)) {
PWRN("Overlap: pending %llu + %zu, request: %llu + %zu", pending[slot].block_number(),
pending[slot].block_count(), 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());
table.fis.fpdma(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();
/* set pending */
Port::write<Sact>(1U << slot);
execute(slot);
}
/*****************
** Port_driver **
*****************/
void handle_irq() override
{
Is::access_t status = Port::read<Is>();
if (verbose)
PDBG("irq: %x state: %u", status, state);
if ((Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) &&
state == IDENTIFY) {
info.construct(device_info);
if (verbose)
info->info();
check_device();
}
/*
* When packets get acked, new ones may be inserted by the block driver
* layer, these new packet requests might be finished during 'ack_packets',
* so clear the IRQ and re-read after 'ack_packets'
*/
while (Is::Sdbs::get(status = Port::read<Is>())) {
ack_irq();
ack_packets();
}
stop();
}
void check_device()
{
if (!info->read<Identity::Sata_caps::Ncq_support>() ||
!hba.ncg()) {
PERR("Device does not support native command queuing: abort");
state_change();
return;
}
cmd_slots = min((int)cmd_slots,
info->read<Identity::Queue_depth::Max_depth >() + 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() { return true; };
Block::Session::Operations ops() override
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
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() override
{
Genode::size_t size = 512;
if (info->read<Identity::Logical_block::Longer_512>())
size = info->read<Identity::Logical_words>() / 2;
return size;
}
Block::sector_t block_count() override
{
return info->read<Identity::Sector_count>();
}
};
#endif /* _ATA_DRIVER_H_ */

View File

@ -0,0 +1,206 @@
/**
* \brief AHCI-port driver for ATAPI devices
* \author Sebastian Sumpf
* \date 2015-04-29
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _ATAPI_DRIVER_H_
#define _ATAPI_DRIVER_H_
#include "ahci.h"
#include <util/endian.h>
using namespace Genode;
struct Atapi_driver : Port_driver
{
unsigned sense_tries = 0;
Block::Packet_descriptor pending;
Atapi_driver(Port &port, Signal_context_capability state_change)
: Port_driver(port, state_change)
{
Port::init();
Port::write<Cmd::Atapi>(1);
read_sense();
}
void atapi_command()
{
Command_header header(command_header_addr(0));
header.atapi_command();
header.clear_byte_count();
execute(0);
}
void test_unit_ready()
{
state = TEST_READY;
Command_table table(command_table_addr(0), 0, 0);
table.fis.atapi();
table.atapi_cmd.test_unit_ready();
atapi_command();
}
void read_sense()
{
state = STATUS;
if (sense_tries++ >= 3) {
PERR("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);
table.fis.atapi();
table.atapi_cmd.read_sense();
atapi_command();
}
void read_capacity()
{
state = IDENTIFY;
addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
Command_table table(command_table_addr(0), phys, 0x1000);
table.fis.atapi();
table.atapi_cmd.read_capacity();
atapi_command();
}
void ack_packets()
{
unsigned slots = Port::read<Ci>();
if (slots & 1 || !pending.valid())
return;
Block::Packet_descriptor p = pending;
pending = Block::Packet_descriptor();
ack_packet(p, true);
}
/*****************
** Port_driver **
*****************/
void handle_irq() override
{
Is::access_t status = Port::read<Is>();
if (verbose) {
PDBG("irq: is: %x ci: %x state: %u", status, Port::read<Ci>(), state);
Device_fis f(fis_base);
PDBG("d2h: status: %x error: %x", f.read<Device_fis::Status>(), f.read<Device_fis::Error>());
}
ack_irq();
if (state == TEST_READY && Port::Is::Dhrs::get(status)) {
Device_fis f(fis_base);
/* check if devic is ready */
if (f.read<Device_fis::Status::Device_ready>() && !f.read<Device_fis::Error>())
read_capacity();
else
read_sense();
}
if (state == READY && Port::Is::Dhrs::get(status)) {
ack_packets();
}
if (Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) {
switch (state) {
case STATUS:
test_unit_ready();
break;
case IDENTIFY:
state = READY;
state_change();
break;
case READY:
ack_packets();
return;
default:
break;
}
}
}
/*****************************
** Block::Driver interface **
*****************************/
bool dma_enabled() { return true; };
Block::Session::Operations ops() override
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
return o;
}
Genode::size_t block_size() override
{
return host_to_big_endian(((unsigned *)device_info)[1]);
}
Block::sector_t block_count() override
{
return host_to_big_endian(((unsigned *)device_info)[0]) + 1;
}
void read_dma(Block::sector_t block_number,
size_t count,
addr_t phys,
Block::Packet_descriptor &packet) override
{
if (pending.valid())
throw Block::Driver::Request_congestion();
sanity_check(block_number, count);
pending = packet;
if (verbose)
PDBG("Add packet read %llu count %zu -> %u", block_number, count, 0);
/* setup fis */
Command_table table(command_table_addr(0), phys, count * block_size());
table.fis.atapi();
/* setup atapi command */
table.atapi_cmd.read10(block_number, count);
/* set and clear write flag in command header */
Command_header header(command_header_addr(0));
header.write<Command_header::Bits::W>(0);
header.clear_byte_count();
/* set pending */
execute(0);
}
};
#endif /* _ATAPI_DRIVER_H_ */

View File

@ -1,3 +0,0 @@
/*
* Dummy compilation unit needed to link a valid target.
*/

File diff suppressed because it is too large Load Diff

View File

@ -1,83 +0,0 @@
/*
* \brief AHCI driver declaration
* \author Martin Stein <martin.stein@genode-labs.com>
* \date 2013-04-10
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _AHCI_DRIVER_H_
#define _AHCI_DRIVER_H_
/* Genode includes */
#include <block/component.h>
#include <ram_session/ram_session.h>
/**
* AHCI driver
*/
class Ahci_driver : public Block::Driver
{
enum { VERBOSE = 0 };
/* import Genode symbols */
typedef Genode::size_t size_t;
typedef Genode::uint64_t uint64_t;
typedef Genode::addr_t addr_t;
int _ncq_command(uint64_t lba, unsigned cnt, addr_t phys, bool w);
public:
/**
* Constructor
*/
Ahci_driver();
/*****************************
** Block::Driver interface **
*****************************/
Block::Session::Operations ops()
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
size_t block_size();
Block::sector_t block_count();
bool dma_enabled() { return true; }
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
return Genode::env()->ram_session()->alloc(size, Genode::UNCACHED); }
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
return Genode::env()->ram_session()->free(c); }
void read_dma(Block::sector_t block_nr, size_t block_cnt, addr_t phys,
Block::Packet_descriptor &packet)
{
if (_ncq_command(block_nr, block_cnt, phys, 0))
throw Io_error();
ack_packet(packet);
}
void write_dma(Block::sector_t block_nr, size_t block_cnt, addr_t phys,
Block::Packet_descriptor &packet)
{
if (_ncq_command(block_nr, block_cnt, phys, 1))
throw Io_error();
ack_packet(packet);
}
};
#endif /* _AHCI_DRIVER_H_ */

View File

@ -1,198 +0,0 @@
/*
* \brief SATA benchmark for Exynos5 platform
* \author Martin Stein
* \date 2012-06-05
*/
/*
* Copyright (C) 2012-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/sleep.h>
#include <base/printf.h>
#include <timer_session/connection.h>
#include <os/attached_ram_dataspace.h>
#include <dataspace/client.h>
/* local includes */
#include <ahci_driver.h>
struct Operation
{
virtual void operator () (Block::Driver &driver,
Genode::addr_t block_number,
Genode::size_t block_count,
Genode::addr_t buffer_phys,
char *buffer_virt) = 0;
};
void print_bench_head()
{
Genode::printf("\n");
Genode::printf("bytes/block bytes sec MB/sec\n");
Genode::printf("----------------------------------------------\n");
}
/*
* \param total_size total number of bytes to read
* \param request_size number of bytes per request
*/
static void run_benchmark(Block::Driver &driver,
Timer::Session &timer,
char *buffer_virt,
Genode::addr_t buffer_phys,
Genode::size_t buffer_size,
Genode::size_t request_size,
Operation &operation)
{
using namespace Genode;
if (request_size > buffer_size) {
PERR("undersized buffer %u, need %u", buffer_size, buffer_size);
while (1) ;
}
size_t const block_count = request_size / driver.block_size();
/*
* The goal is to get 5 test repetitions that took 2 s < time < 2.3 s,
* each. Thus we start with count = 32 and then adjust the count
* for a retry if the test time is not in range.
*/
unsigned tmp_bytes = 64 * request_size;;
unsigned bytes = 0;
unsigned ms = 0;
unsigned reps = 0;
float sec = 0;
float mb_per_sec = 0;
while (1)
{
size_t num_requests = tmp_bytes / request_size;
/* do measurement */
unsigned const time_before_ms = timer.elapsed_ms();
for (unsigned i = 0; i < num_requests; i++)
{
addr_t const block_number = i * block_count;
operation(driver, block_number, block_count,
buffer_phys, buffer_virt);
}
/* read results */
unsigned const time_after_ms = timer.elapsed_ms();
ms = time_after_ms - time_before_ms;
/* check if test time in range */
if (ms < 2000 || ms >= 2300) {
/*
* adjust transfer amount according to measured time
*
* FIXME implement static inertia
*/
tmp_bytes = (((float) 2150 / ms) * tmp_bytes);
tmp_bytes &= 0xfffffe00; /* align to 512 byte blocks */
} else {
/* if new result is better than the last one do update */
float const tmp_mb = ((float)tmp_bytes / 1000) / 1000;
float const tmp_sec = (float)ms / 1000;
float const tmp_mb_per_sec = (float)tmp_mb / tmp_sec;
if (tmp_mb_per_sec > mb_per_sec) {
sec = tmp_sec;
mb_per_sec = tmp_mb_per_sec;
bytes = tmp_bytes;
}
/* check if we need more repetitions */
reps++;
if (reps == 5) break;
}
}
unsigned const sec_left = sec;
unsigned const sec_right = 1000 * ((float)sec - sec_left);
unsigned const mb_per_sec_left = mb_per_sec;
unsigned const mb_per_sec_right = 1000 * ((float)mb_per_sec - mb_per_sec_left);
PLOG(" %10u %10u %u.%03u %10u.%03u", request_size, bytes, sec_left, sec_right, mb_per_sec_left, mb_per_sec_right);
}
int main(int argc, char **argv)
{
using namespace Genode;
printf("AHCI bench\n");
printf("==========\n");
printf("\n");
static Ahci_driver driver;
static Timer::Connection timer;
long const request_sizes[] = {
1048576, 262144, 16384, 8192, 4096, 2048, 1024, 512, 0 };
/* total size of communication buffer */
size_t const buffer_size = 1024*1024;
/* allocate read/write buffer */
static Attached_ram_dataspace buffer(env()->ram_session(), buffer_size, Genode::UNCACHED);
char * const buffer_virt = buffer.local_addr<char>();
addr_t const buffer_phys = Dataspace_client(buffer.cap()).phys_addr();
/*
* Benchmark reading from SATA device
*/
printf("read\n");
printf("~~~~\n");
print_bench_head();
struct Read : Operation
{
void operator () (Block::Driver &driver,
addr_t number, size_t count, addr_t phys, char *virt)
{
Block::Packet_descriptor packet;
if (driver.dma_enabled())
driver.read_dma(number, count, phys, packet);
else
driver.read(number, count, virt, packet);
}
} read_operation;
for (unsigned i = 0; request_sizes[i]; i++)
run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
request_sizes[i], read_operation);
/*
* Benchmark writing to SATA device
*
* Attention: Original data will be overridden on target drive
*/
printf("\n");
printf("write\n");
printf("~~~~~\n");
print_bench_head();
struct Write : Operation
{
void operator () (Block::Driver &driver,
addr_t number, size_t count, addr_t phys, char *virt)
{
Block::Packet_descriptor packet;
if (driver.dma_enabled())
driver.write_dma(number, count, phys, packet);
else
driver.write(number, count, virt, packet);
}
} write_operation;
for (unsigned i = 0; request_sizes[i]; i++)
run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
request_sizes[i], write_operation);
printf("\n");
printf("benchmark finished\n");
sleep_forever();
return 0;
}

View File

@ -1,6 +0,0 @@
TARGET = ahci_bench
REQUIRES = exynos5
SRC_CC = main.cc ahci_driver.cc
LIBS = base
INC_DIR += $(PRG_DIR)/.. $(PRG_DIR)/../..
vpath ahci_driver.cc $(PRG_DIR)/..

View File

@ -0,0 +1,385 @@
/**
* \brief Driver for Exynos-5250 soc
* \author Martin Stein
* \author Sebastian Sumpf
* \date 2015-05-04
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <drivers/board_base.h>
#include <irq_session/connection.h>
#include <regulator/consts.h>
#include <regulator_session/connection.h>
#include <ahci.h>
using namespace Genode;
/**
* I2C master interface
*/
struct I2c_interface : Attached_mmio
{
enum { TX_DELAY_US = 1 };
/********************************
** MMIO structure description **
********************************/
struct Start_msg : Genode::Register<8>
{
struct Addr : Bitfield<1, 7> { };
};
struct Con : Register<0x0, 8>
{
struct Tx_prescaler : Bitfield<0, 4> { };
struct Irq_pending : Bitfield<4, 1> { };
struct Irq_en : Bitfield<5, 1> { };
struct Clk_sel : Bitfield<6, 1> { };
struct Ack_en : Bitfield<7, 1> { };
};
struct Stat : Register<0x4, 8>
{
struct Last_bit : Bitfield<0, 1> { };
struct Arbitr : Bitfield<3, 1> { };
struct Txrx_en : Bitfield<4, 1> { };
struct Busy : Bitfield<5, 1> { };
struct Mode : Bitfield<6, 2> { };
};
struct Add : Register<0x8, 8>
{
struct Slave_addr : Bitfield<0, 8> { };
};
struct Ds : Register<0xc, 8> { };
struct Lc : Register<0x10, 8>
{
struct Sda_out_delay : Bitfield<0, 2> { };
struct Filter_en : Bitfield<2, 1> { };
};
Mmio::Delayer &delayer;
/* single-word message that starts a multi-word message transfer */
Start_msg::access_t const start_msg;
/**
* Constructor
*
* \param base physical MMIO base
* \param slave_addr ID of the targeted slave
*/
I2c_interface(addr_t base, unsigned slave_addr, Mmio::Delayer &delayer)
: Attached_mmio(base, 0x10000), delayer(delayer),
start_msg(Start_msg::Addr::bits(slave_addr))
{ }
/**
* Wether acknowledgment for last transaction can be received
*/
bool ack_received()
{
for (unsigned i = 0; i < 3; i++) {
if (read<Con::Irq_pending>() && !read<Stat::Last_bit>()) return 1;
delayer.usleep(TX_DELAY_US);
}
PERR("I2C ack not received");
return 0;
}
/**
* Wether arbitration errors occured during the last transaction
*/
bool arbitration_error()
{
if (read<Stat::Arbitr>()) {
PERR("I2C arbitration failed");
return 1;
}
return 0;
}
/**
* Let I2C master send a message to I2C slave
*
* \param msg message base
* \param msg_size message size
*
* \retval 0 call was successful
* \retval <0 call failed, error code
*/
int send(uint8_t * msg, size_t msg_size)
{
/* initiate message transfer */
if (!wait_for<Stat::Busy>(0, delayer)) {
PERR("I2C busy");
return -1;
}
Stat::access_t stat = read<Stat>();
Stat::Txrx_en::set(stat, 1);
Stat::Mode::set(stat, 3);
write<Stat>(stat);
write<Ds>(start_msg);
delayer.usleep(1000);
write<Con::Tx_prescaler>(11);
write<Stat::Busy>(1);
/* transmit message payload */
for (unsigned i = 0; i < msg_size; i++) {
if (!ack_received()) return -1;
write<Ds>(msg[i]);
delayer.usleep(TX_DELAY_US);
write<Con::Irq_pending>(0);
if (arbitration_error()) return -1;
}
/* end message transfer */
if (!ack_received()) return -1;
write<Stat::Busy>(0);
write<Con::Irq_en>(0);
write<Con::Irq_pending>(0); /* FIXME fixup */
if (arbitration_error()) return -1;
if (!wait_for<Stat::Busy>(0, delayer)) {
PERR("I2C end transfer failed");
return -1;
}
return 0;
}
};
/**
* I2C control interface of SATA PHY-layer controller
*/
struct I2c_sataphy : I2c_interface
{
enum { SLAVE_ADDR = 0x38 };
/**
* Constructor
*/
I2c_sataphy(Mmio::Delayer &delayer)
: I2c_interface(0x121d0000, SLAVE_ADDR, delayer)
{ }
/**
* Enable the 40-pin interface of the SATA PHY controller
*
* \retval 0 call was successful
* \retval <0 call failed, error code
*/
int enable_40_pins()
{
/*
* I2C message
*
* first byte: set address
* second byte: set data
*/
static uint8_t msg[] = { 0x3a, 0x0b };
enum { MSG_SIZE = sizeof(msg)/sizeof(msg[0]) };
/* send messaage */
if (send(msg, MSG_SIZE)) return -1;
if (verbose) printf("SATA PHY 40-pin interface enabled\n");
return 0;
}
/**
* Get I2C interface ready for transmissions
*/
void init()
{
write<Add::Slave_addr>(SLAVE_ADDR);
Con::access_t con = read<Con>();
Con::Irq_en::set(con, 1);
Con::Ack_en::set(con, 1);
Con::Clk_sel::set(con, 1);
Con::Tx_prescaler::set(con, 9);
write<Con>(con);
Lc::access_t lc = 0;
Lc::Sda_out_delay::set(lc, 3);
Lc::Filter_en::set(lc, 1);
write<Lc>(lc);
}
};
/**
* Classical control interface of SATA PHY-layer controller
*/
struct Sata_phy_ctrl : Attached_mmio
{
Mmio::Delayer &delayer;
I2c_sataphy i2c_sataphy { delayer };
/********************************
** MMIO structure description **
********************************/
struct Reset : Register<0x4, 32>
{
struct Global : Bitfield<1, 1> { };
struct Non_link : Bitfield<0, 8> { };
struct Link : Bitfield<16, 4> { };
};
struct Mode0 : Register<0x10, 32>
{
struct P0_phy_spdmode : Bitfield<0, 2> { };
};
struct Ctrl0 : Register<0x14, 32>
{
struct P0_phy_calibrated : Bitfield<8, 1> { };
struct P0_phy_calibrated_sel : Bitfield<9, 1> { };
};
struct Phctrlm : Register<0xe0, 32>
{
struct High_speed : Bitfield<0, 1> { };
struct Ref_rate : Bitfield<1, 1> { };
};
struct Phstatm : Register<0xf0, 32>
{
struct Pll_locked : Bitfield<0, 1> { };
};
/**
* Constructor
*/
Sata_phy_ctrl(Mmio::Delayer &delayer)
: Attached_mmio(0x12170000, 0x10000), delayer(delayer)
{
i2c_sataphy.init();
}
/**
* Initialize parts of SATA PHY that are controlled classically
*
* \retval 0 call was successful
* \retval <0 call failed, error code
*/
int init()
{
/* reset */
write<Reset>(0);
write<Reset::Non_link>(~0);
write<Reset::Link>(~0);
write<Reset::Global>(~0);
/* set up SATA phy generation 3 (6 Gb/s) */
Phctrlm::access_t phctrlm = read<Phctrlm>();
Phctrlm::Ref_rate::set(phctrlm, 0);
Phctrlm::High_speed::set(phctrlm, 1);
write<Phctrlm>(phctrlm);
Ctrl0::access_t ctrl0 = read<Ctrl0>();
Ctrl0::P0_phy_calibrated::set(ctrl0, 1);
Ctrl0::P0_phy_calibrated_sel::set(ctrl0, 1);
write<Ctrl0>(ctrl0);
write<Mode0::P0_phy_spdmode>(2);
if (i2c_sataphy.enable_40_pins()) return -1;
/* Release reset */
write<Reset::Global>(0);
write<Reset::Global>(1);
/*
* FIXME Linux reads this bit once only and continues
* directly, also with zero. So if we get an error
* at this point we should study the Linux behavior
* in more depth.
*/
if (!wait_for<Phstatm::Pll_locked>(1, delayer)) {
PERR("PLL lock failed");
return -1;
}
if (verbose)
printf("SATA PHY initialized\n");
return 0;
}
};
struct Exynos5_hba : Platform::Hba
{
Irq_connection irq { Board_base::SATA_IRQ };
Regulator::Connection clock_src { Regulator::CLK_SATA };
Regulator::Connection power_src { Regulator::PWR_SATA };
Exynos5_hba(Mmio::Delayer &delayer)
{
clock_src.state(true);
power_src.state(true);
Sata_phy_ctrl phy(delayer);
if (phy.init())
throw Root::Unavailable();
/* additionally perform some generic initializations */
::Hba hba(*this);
::Hba::Cap::access_t cap = hba.read< ::Hba::Cap>();
::Hba::Cap2::access_t cap2 = hba.read< ::Hba::Cap2>();
/* reset */
hba.write< ::Hba::Ghc::Hr>(1);
if (!hba.wait_for< ::Hba::Ghc::Hr>(0, ::Hba::delayer(), 1000, 1000)) {
PERR("HBA reset failed");
throw Root::Unavailable();
}
hba.write< ::Hba::Cap>(cap);
hba.write< ::Hba::Cap2>(cap2);
/* for exynos set port 0 as implemented, usally set by BIOS */
hba.write< ::Hba::Pi>(0x1);
}
/*******************
** Hba interface **
*******************/
Genode::addr_t base() const override { return 0x122f0000; }
Genode::size_t size() const override { return 0x10000; }
void sigh_irq(Signal_context_capability sigh) override
{
irq.sigh(sigh);
ack_irq();
}
void ack_irq() override { irq.ack_irq(); }
Ram_dataspace_capability
alloc_dma_buffer(size_t size) override
{
return env()->ram_session()->alloc(size, UNCACHED);
}
void free_dma_buffer(Ram_dataspace_capability ds)
{
env()->ram_session()->free(ds);
}
};
Platform::Hba &Platform::init(Mmio::Delayer &delayer)
{
static Exynos5_hba h(delayer);
return h;
}

View File

@ -1,578 +0,0 @@
/*
* \brief Generic base of AHCI device
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
* \author Martin Stein <Martin.Stein@genode-labs.com>
* \date 2011-08-10
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _AHCI_DEVICE_BASE_H_
#define _AHCI_DEVICE_BASE_H_
/* Genode includes */
#include <base/printf.h>
#include <base/sleep.h>
#include <block/driver.h>
#include <block/component.h>
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <irq_session/connection.h>
#include <io_mem_session/connection.h>
#include <timer_session/connection.h>
/**
* Enable for debugging output
*/
static const bool verbose = false;
using namespace Genode;
/**
* Base class for register access
*/
class Reg_base
{
protected:
uint32_t _value(uint32_t offset) { return *(volatile uint32_t *)(this + offset); }
void _set(uint32_t offset, uint32_t val) { *(volatile uint32_t *)(this + offset) = val; }
};
/*
* HBA: Generic Host Control
*/
class Generic_ctrl : public Reg_base
{
public:
/* host capabilities */
uint32_t hba_cap() { return _value(0x0); }
/* return port count from hba_cap */
uint32_t port_count() { return (hba_cap() & 0x1f) + 1; }
/* return command slot count from hba_cap */
uint32_t cmd_slots() { return ((hba_cap() >> 8) & 0x1f) + 1; }
/* global host control */
uint32_t hba_ctrl() { return _value(0x4); }
void hba_ctrl(uint32_t val) { _set(0x4, val); }
/* set Interrupt Enable (IE) in hba_ctrl */
void global_interrupt_enable()
{
hba_ctrl(hba_ctrl() | 2);
if (verbose)
PDBG("HBA %x", hba_ctrl());
}
/* set AHCI enable (AE) in hba_ctrl */
void global_enable_ahci()
{
if (!(hba_ctrl() & (1 << 31)))
hba_ctrl(hba_ctrl() | (1 << 31));
if (verbose)
PDBG("AHCI ENABLED: %x", hba_ctrl());
}
/* global interrupt status (contains port interrupts) */
uint32_t hba_intr_status() { return _value(0x8); }
void hba_intr_status(uint32_t val) { return _set(0x8, val); }
/* acknowledge global interrupt */
void hba_interrupt_ack() { hba_intr_status(hba_intr_status()); }
/* AHCI version */
uint32_t version() { return _value(0x10); }
};
/*
* AHCI port registers (one set per port)
*/
class Ahci_port : public Reg_base
{
public:
/* command list base */
void cmd_list_base(addr_t cmd_base)
{
uint64_t addr = cmd_base;
_set(0x0, addr);
_set(0x4, addr >> 32);
}
/* receive FIS base address */
void fis_base(addr_t fis_base)
{
uint64_t addr = fis_base;
_set(0x8, addr);
_set(0xc, addr >> 32);
}
/* interrupt status */
uint32_t intr_status() { return _value(0x10); }
void intr_status(uint32_t val) { _set(0x10, val); }
/* interrupt enable */
void intr_enable(uint32_t val) { _set(0x14, val); }
/* command */
uint32_t cmd() { return _value(0x18); }
void cmd(uint32_t val) { _set(0x18, val); }
/* task file data */
uint32_t tfd() { return _value(0x20); }
/* Serial ATA status */
uint32_t status() { return _value(0x28); }
/* Serial ATA control */
void sctl(uint32_t val) { _set(0x2c, val); }
uint32_t sctl() { return _value(0x2c); }
/* Serial ATA error */
void err(uint32_t val) { _set(0x30, val); }
uint32_t err() { return _value(0x30); }
/* command issue (1 bit per command slot) */
void cmd_issue(uint32_t val) { _set(0x38, val); }
uint32_t cmd_issue() { return _value(0x38); }
/**
* Check if device is active
*/
bool status_active()
{
enum {
PRESENT_ESTABLISHED = 0x3, /* device is present and connection is established */
PM_ACTIVE = 0x100, /* interface is in active power-mngmt. state */
PM_PARTIRL = 0x200, /* interface is in partial power-mngmt. state */
PM_SLUMBER = 0x600, /* interface is in slumber power-mngmt. state */
};
uint32_t stat = status();
uint32_t pm_stat = stat & 0xf00;
/* if controller is in sleep state, try to wake up */
if (pm_stat == PM_PARTIRL || pm_stat == PM_SLUMBER) {
if (verbose)
PDBG("Controller is in sleep state, trying to wake up ...");
cmd(cmd() | (1 << 28));
while (!(stat & PM_ACTIVE) || (stat & 0xf) != PRESENT_ESTABLISHED) { stat = status(); }
}
return (((stat & 0xf) == PRESENT_ESTABLISHED) && (stat & PM_ACTIVE));
}
/**
* Enable CMD.ST to start command list processing
*/
void hba_enable()
{
enum {
STS_BSY = 0x80, /* device is busy */
STS_DRQ = 0x08, /* data transfer requested */
};
while (tfd() & (STS_BSY | STS_DRQ))
if (verbose)
PDBG("TFD %x", tfd());
cmd(cmd() | 1);
}
/**
* Disable CMD.ST
*/
void hba_disable()
{
if ((cmd() & 1) && !(cmd_issue() & 1))
cmd(cmd() & ~1);
}
/**
* Enable port interrupts
*/
void interrupt_enable() { intr_enable(~0U); }
/**
* Acknowledge port interrupts
*/
uint32_t interrupt_ack()
{
interrupt_pm_ack();
uint32_t status = intr_status();
intr_status(status);
return status;
}
/**
* Check and handle interrupts due to power mgmt. state transitions
*/
void interrupt_pm_ack()
{
enum {
INT_PORT_CON_STATUS = 0x40,
INT_PHY_RDY_STATUS = 0x400000,
};
uint32_t status = intr_status();
if (status & INT_PORT_CON_STATUS)
/* clear DIAG.x */
err(err() & ~(1 << 26));
if (status & INT_PORT_CON_STATUS)
/* clear DIAG.n */
err(err() & ~(1 << 16));
}
/**
* Disable power mgmt. set SCTL.IPM to 3
*/
void disable_pm() { sctl(sctl() | (3 << 8)); }
/**
* Power up device
*/
void get_ready()
{
enum {
SPIN_UP_DEVICE = 0x2,
POWER_ON_DEVICE = 0x4,
FIS_RECV_ENABLE = 0x10,
ENABLE = SPIN_UP_DEVICE | POWER_ON_DEVICE | FIS_RECV_ENABLE
};
cmd(cmd() | ENABLE);
}
/**
* Reset this port
*/
void reset()
{
/* check for ST bit in command register */
if (cmd() & 1)
PWRN("CMD.ST bit set during device reset --> unknown behavior");
/* set device initialization bit for at least 1ms */
sctl((sctl() & ~0xf) | 1);
Timer::Connection timer;
timer.msleep(1);
sctl(sctl() & ~0xf);
while ((status() & 0xf) != 0x3) {}
}
/**
* Return the size of this structure
*/
static uint32_t size() { return 0x80; }
};
/**
* AHCI command list structure
*/
struct Command_list
{
uint8_t cfl:5; /* Command FIS length */
uint8_t a:1; /* ATAPI command flag */
uint8_t w:1; /* Write flag */
uint8_t p:1; /* Prefetchable flag */
uint8_t unsused; /* we don't use byte 2 yet */
uint16_t prdtl; /* Physical region descr. length */
uint32_t prdbc; /* PRD byte count */
uint32_t cmd_table_base_l; /* Command table base addr (low) */
uint32_t cmd_table_base_u;
uint32_t reserved[0];
};
/**
* AHCI command table structure
*/
struct Command_table
{
/**
* Setup FIS and PRD
*/
void setup_command(uint8_t cmd, uint64_t lba48, uint16_t blk_cnt, addr_t phys_addr)
{
enum { MAX_BYTES = 1 << 22 }; /* 4MB = one PRD */
uint8_t *fis = (uint8_t *)this;
uint64_t addr = phys_addr;
/* setup FIS */
fis[0] = 0x27; /* type = host to device */
fis[1] = 0x80; /* set update command flag */
fis[2] = cmd; /* actual command */
fis[4] = lba48 & 0xff; /* LBA 0 - 7 */
fis[5] = (lba48 >> 8) & 0xff; /* LBA 8 - 15 */
fis[6] = (lba48 >> 16) & 0xff; /* LBA 16 - 23 */
fis[7] = 0x40; /* LBA mode flag */
fis[8] = (lba48 >> 24) & 0xff; /* LBA 24 - 31 */
fis[9] = (lba48 >> 32) & 0xff; /* LBA 32 - 39 */
fis[10] = (lba48 >> 40) & 0xff; /* LBA 40 - 47 */
fis[12] = blk_cnt & 0xff; /* sector count 0 - 7 */
fis[13] = (blk_cnt >> 8) & 0xff; /* sector count 8 - 15 */
/* setup PRD for DMA */
uint32_t addr_l = addr;
uint32_t addr_u = addr >> 32;
memcpy(&fis[0x80], &addr_l, 4); /* DBA: data base address */
memcpy(&fis[0x84], &addr_u, 4); /* DBA: data base address upper */
uint32_t bytes = (blk_cnt * 512) - 1;
if (bytes + 1 > MAX_BYTES) {
PERR("Unsupported request size %u > %u", bytes, MAX_BYTES);
throw Block::Driver::Io_error();
}
/* set byte count for PRD 22 bit */
fis[0x8c] = bytes & 0xff;
fis[0x8d] = (bytes >> 8) & 0xff;
fis[0x8e] = (bytes >> 16) & 0x3f;
}
};
/**
* Generic base of AHCI device
*/
class Ahci_device_base
{
protected:
enum {
AHCI_PORT_BASE = 0x100,
};
Generic_ctrl *_ctrl; /* generic host control */
Ahci_port *_port; /* port base of device */
Irq_session_client *_irq; /* device IRQ */
Genode::Signal_receiver _irq_rec; /* IRQ signal receiver */
Genode::Signal_context _irq_ctx; /* IRQ signal context */
size_t _block_cnt; /* number of blocks on device */
Command_list *_cmd_list; /* pointer to command list */
Command_table *_cmd_table; /* pointer to command table */
Ram_dataspace_capability _ds; /* backing-store of internal data structures */
Io_mem_session_capability _io_cap; /* I/O mem cap */
/**
* Find first non-ATAPI device that is ready
*/
bool _scan_ports()
{
uint32_t port_cnt = _ctrl->port_count();
Ahci_port *port = (Ahci_port *)((char *)_ctrl + AHCI_PORT_BASE);
for (uint32_t i = 0;
i <= port_cnt;
i++, port = (Ahci_port *)((char *)port + Ahci_port::size())) {
bool is_atapi = port->cmd() & (1 << 24); /* check bit 24 */
PINF("Port %u: ATAPI %s", i, is_atapi ? "yes" : "no");
if (is_atapi)
continue;
/* port status */
if (port->status_active()) {
PINF("Port %u: Detected interface is active", i);
_port = port;
return true;
}
}
return false;
}
void _setup_memory()
{
_ds = alloc_dma_buffer(0x1000);
addr_t phys = Dataspace_client(_ds).phys_addr();
uint8_t *virt = (uint8_t *)env()->rm_session()->attach(_ds);
/* setup command list (size 1k naturally aligned) */
_port->cmd_list_base(phys);
_cmd_list = (struct Command_list *)(virt);
/* for now we transfer one PRD with a FIS size of 5 byte */
_cmd_list->prdtl = 1;
_cmd_list->cfl = 5;
virt += 1024; phys += 1024;
/* setup received FIS base (256 byte naturally aligned) */
_port->fis_base(phys);
virt += 256; phys += 256;
uint64_t addr = phys;
/* setup command table (128 byte aligned (cache line size)) */
_cmd_list->cmd_table_base_l = addr;
_cmd_list->cmd_table_base_u = addr >> 32;
_cmd_table = (struct Command_table *)(virt);
}
/**
* Execute a prepared command
*/
void _execute_command()
{
/* reset byte count */
_cmd_list->prdbc = 0;
/* start HBA command processing */
_port->hba_enable();
if (verbose)
PDBG("Int status: global: %x port: %x error: %x",
_ctrl->hba_intr_status(), _port->intr_status(), _port->err());
/* write CI (command issue) slot 0 */
_port->cmd_issue(1);
uint32_t status = 0;
while (!status) {
/* wait for interrupt */
_irq_rec.wait_for_signal();
if (verbose)
PDBG("Int status (IRQ): global: %x port: %x error: %x",
_ctrl->hba_intr_status(), _port->intr_status(), _port->err());
/* acknowledge interrupt */
status = _port->interrupt_ack();
}
/* check for error */
enum {
INT_SETUP_FIS_DMA = 0x4,
INT_SETUP_FIS_PIO = 0x2,
INT_HOST_REGISTER_FIS = 0x1,
INT_OK = INT_SETUP_FIS_DMA | INT_SETUP_FIS_PIO | INT_HOST_REGISTER_FIS
};
if (!(status & INT_OK)) {
PERR("Error during SATA request (irq state %x)", status);
throw Block::Driver::Io_error();
}
/* acknowledge global port interrupt */
_ctrl->hba_interrupt_ack();
_irq->ack_irq();
/* disable hba */
_port->hba_disable();
}
/**
* Execute ATA 'IDENTIFY DEVICE' command
*/
void _identify_device()
{
Ram_dataspace_capability ds = alloc_dma_buffer(0x1000);
uint16_t *dev_info = (uint16_t *)env()->rm_session()->attach(ds);
enum { IDENTIFY_DEVICE = 0xec };
try {
addr_t phys = Dataspace_client(ds).phys_addr();
_cmd_table->setup_command(IDENTIFY_DEVICE, 0, 0, phys);
_execute_command();
/* XXX: just read 32 bit for now */
_block_cnt = *((size_t *)&dev_info[100]);
} catch (Block::Driver::Io_error) {
PERR("I/O Error: Could not identify device");
}
if (verbose)
PDBG("Max LBA48 block: %zu", _block_cnt);
env()->rm_session()->detach(dev_info);
env()->ram_session()->free(ds);
}
Ahci_device_base(addr_t base, Io_mem_session_capability io_cap)
: _ctrl((Generic_ctrl *)base), _port(0), _irq(0), _cmd_list(0),
_cmd_table(0), _io_cap(io_cap) { }
public:
virtual ~Ahci_device_base()
{
/* delete internal data structures */
if (_ds.valid()) {
env()->rm_session()->detach((void*)_cmd_list);
env()->ram_session()->free(_ds);
}
/* close I/O mem session */
env()->rm_session()->detach((void *)_ctrl);
env()->parent()->close(_io_cap);
/* XXX release _pci_device */
/* close IRQ session */
destroy(env()->heap(), _irq);
}
static size_t block_size() { return 512; }
size_t block_count() { return _block_cnt; }
/**
* Issue ATA 'READ_DMA_EXT' command
*/
void read(Block::sector_t block_number, size_t block_count,
addr_t phys)
{
_cmd_list->w = 0;
enum { READ_DMA_EXT = 0x25 };
_cmd_table->setup_command(READ_DMA_EXT, block_number,
block_count, phys);
_execute_command();
}
/**
* Issue ATA 'WRITE_DMA_EXT' command
*/
void write(Block::sector_t block_number, size_t block_count,
addr_t phys)
{
_cmd_list->w = 1;
enum { WRITE_DMA_EXT = 0x35 };
_cmd_table->setup_command(WRITE_DMA_EXT, block_number,
block_count, phys);
_execute_command();
}
virtual Ram_dataspace_capability alloc_dma_buffer(size_t size) = 0;
virtual void free_dma_buffer(Ram_dataspace_capability cap) = 0;
};
#endif /* _AHCI_DEVICE_BASE_H_ */

View File

@ -1,90 +0,0 @@
/*
* \brief Generic base of AHCI driver
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
* \author Martin Stein <Martin.Stein@genode-labs.com>
* \date 2011-08-10
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _AHCI_DRIVER_BASE_H_
#define _AHCI_DRIVER_BASE_H_
/* local includes */
#include <ahci_device.h>
/**
* Implementation of block driver interface
*/
class Ahci_driver_base : public Block::Driver
{
protected:
Ahci_device *_device;
void _sanity_check(size_t block_number, size_t count)
{
if (!_device || (block_number + count > block_count()))
throw Io_error();
}
Ahci_driver_base(Ahci_device * const device)
: _device(device) { }
public:
~Ahci_driver_base()
{
if (_device)
destroy(env()->heap(), _device);
}
size_t block_size() { return Ahci_device::block_size(); }
Block::sector_t block_count() {
return _device ? _device->block_count() : 0; }
Block::Session::Operations ops()
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
bool dma_enabled() { return true; }
void read_dma(Block::sector_t block_number,
size_t block_count,
addr_t phys,
Block::Packet_descriptor &packet)
{
_sanity_check(block_number, block_count);
_device->read(block_number, block_count, phys);
ack_packet(packet);
}
void write_dma(Block::sector_t block_number,
size_t block_count,
addr_t phys,
Block::Packet_descriptor &packet)
{
_sanity_check(block_number, block_count);
_device->write(block_number, block_count, phys);
ack_packet(packet);
}
Ram_dataspace_capability alloc_dma_buffer(size_t size) {
return _device->alloc_dma_buffer(size); }
void free_dma_buffer(Ram_dataspace_capability cap) {
return _device->free_dma_buffer(cap); }
};
#endif /* _AHCI_DRIVER_BASE_H_ */

View File

@ -1,63 +1,158 @@
/*
* \brief Minimal AHCI-ATA driver
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
* \date 2011-08-10
*
* This driver currently supports only one command slot, one FIS, and one PRD
* per FIS, thus limiting the request size to 4MB per request. Since the packet
* interface currently only supports a synchronous mode of operation the above
* limitations seems reasonable.
/**
* \brief Block driver session creation
* \author Sebastian Sumpf
* \date 2015-09-29
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <block/component.h>
#include <os/config.h>
#include <os/server.h>
#include <util/xml_node.h>
/* local includes */
#include <ahci_driver.h>
#include "ahci.h"
using namespace Genode;
namespace Block {
class Factory;
class Root_multiple_clients;
}
struct Block::Factory : Driver_factory
{
long device_num;
Block::Driver *create()
{
return Ahci_driver::claim_port(device_num);
}
void destroy(Block::Driver *driver)
{
Ahci_driver::free_port(device_num);
}
Factory(long device_num) : device_num(device_num) { }
};
class Session_component : public Block::Session_component
{
public:
Session_component(Block::Driver_factory &driver_factory,
Server::Entrypoint &ep, Genode::size_t buf_size)
: Block::Session_component(driver_factory, ep, buf_size) { }
Block::Driver_factory &factory() { return _driver_factory; }
};
class Block::Root_multiple_clients : public Root_component< ::Session_component>,
public Ahci_root
{
private:
Server::Entrypoint &_ep;
long _device_num(const char *session_label)
{
long num = -1;
try {
using namespace Genode;
Xml_node policy = Genode::config()->xml_node().sub_node("policy");
for (;; policy = policy.next("policy")) {
char label_buf[64];
policy.attribute("label").value(label_buf, sizeof(label_buf));
if (Genode::strcmp(session_label, label_buf))
continue;
/* read device attribute */
policy.attribute("device").value(&num);
break;
}
} catch (...) {}
return num;
}
protected:
::Session_component *_create_session(const char *args)
{
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
/* TODO: build quota check */
/* Search for configured device */
char label_buf[64];
Genode::Arg_string::find_arg(args,
"label").string(label_buf,
sizeof(label_buf),
"<unlabeled>");
long num = _device_num(label_buf);
if (num < 0) {
PERR("No confguration found for client: %s", label_buf);
throw Root::Invalid_args();
}
if (!Ahci_driver::is_avail(num)) {
PERR("Device %ld not avaiable", num);
throw Root::Unavailable();
}
Factory *factory = new (Genode::env()->heap()) Factory(num);
return new (md_alloc()) ::Session_component(*factory,
_ep, tx_buf_size);
}
void _destroy_session(::Session_component *session)
{
Driver_factory &factory = session->factory();
destroy(env()->heap(), session);
destroy(env()->heap(), &factory);
}
public:
Root_multiple_clients(Server::Entrypoint &ep, Allocator *md_alloc)
: Root_component(&ep.rpc_ep(), md_alloc), _ep(ep) { }
Server::Entrypoint &entrypoint() override { return _ep; }
void announce() override
{
Genode::env()->parent()->announce(_ep.manage(*this));
}
};
struct Main
{
Server::Entrypoint &ep;
struct Factory : Block::Driver_factory
{
Block::Driver *create() {
static Ahci_driver driver;
return &driver; }
void destroy(Block::Driver *driver) { }
} factory;
Block::Root root;
Block::Root_multiple_clients root;
Main(Server::Entrypoint &ep)
: ep(ep), root(ep, Genode::env()->heap(), factory)
: root(ep, Genode::env()->heap())
{
printf("--- AHCI driver started ---\n");
Genode::env()->parent()->announce(ep.manage(root));
PINF("--- Starting AHCI driver -> done right .-) --\n");
Ahci_driver::init(root);
}
};
/************
** Server **
************/
namespace Server {
char const *name() { return "ahci_ep"; }
size_t stack_size() { return 2*1024*sizeof(long); }
void construct(Entrypoint &ep) { static Main server(ep); }
char const *name() { return "ahci_ep"; }
size_t stack_size() { return 2 * 1024 * sizeof(long); }
void construct(Entrypoint &ep) { static Main server(ep); }
}

View File

@ -1,3 +1,3 @@
TARGET = ahci
LIBS += ahci server
SRC_CC += empty.cc
TARGET = ahci_drv
SRC_CC = main.cc ahci.cc
LIBS += server base config ahci_platform

View File

@ -1,209 +0,0 @@
/*
* \brief AHCI device
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
* \author Martin Stein <Martin.Stein@genode-labs.com>
* \date 2011-08-10
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _AHCI_DEVICE_H_
#define _AHCI_DEVICE_H_
/* Genode includes */
#include <pci_session/connection.h>
#include <pci_device/client.h>
/* local includes */
#include <ahci_device_base.h>
/**
* AHCI device
*/
class Ahci_device : public Ahci_device_base
{
private:
enum Pci_config {
PCI_CFG_BMIBA_OFF = 0x24, /* offset in PCI config space */
CLASS_MASS_STORAGE = 0x10000U,
SUBCLASS_AHCI = 0x0600U,
CLASS_MASK = 0xffff00U,
AHCI_BASE_ID = 0x5, /* resource id of AHCI base addr <BAR 5> */
AHCI_INTR_OFF = 0x3c, /* offset of interrupt information in config space */
PCI_CFG_CMD_REG = 0x4,
};
::Pci::Connection &_pci;
::Pci::Device_client *_pci_device;
/**
* Return next PCI device
*/
static Pci::Device_capability _scan_pci(Pci::Connection &pci, Pci::Device_capability prev_device_cap)
{
Pci::Device_capability device_cap;
for (unsigned i = 0; i < 2; i++) {
try {
device_cap = pci.next_device(prev_device_cap,
CLASS_MASS_STORAGE | SUBCLASS_AHCI,
CLASS_MASK);
break;
} catch (Pci::Device::Quota_exceeded) {
Genode::env()->parent()->upgrade(pci.cap(), "ram_quota=4096");
}
}
if (prev_device_cap.valid())
pci.release_device(prev_device_cap);
return device_cap;
}
/**
* Initialize port
*/
void _init(::Pci::Device_client *pci_device)
{
uint32_t version = _ctrl->version();
PINF("AHCI Version: %x.%04x", (version >> 16), version & 0xffff);
/* HBA capabilities at offset 0 */
uint32_t caps = _ctrl->hba_cap();
PINF("CAPs:");
PINF("\tPort count: %u", _ctrl->port_count());
PINF("\tCommand slots: %u", _ctrl->cmd_slots());
PINF("\tAHCI only: %s", (caps & 0x20000) ? "yes" : "no");
PINF("\tNative command queuing: %s", (caps & 0x40000000) ? "yes" : "no");
PINF("\t64 Bit: %s", (caps & 0x80000000) ? "yes" : "no");
/* setup up AHCI data structures */
_setup_memory();
/* check and possibly enable AHCI mode */
_ctrl->global_enable_ahci();
/* enable global interrupts */
_ctrl->global_interrupt_enable();
/* disable power mgmt. */
_port->disable_pm();
/* startup device */
_port->get_ready();
/* reset port */
_port->reset();
/* clear error register */
_port->err(_port->err());
/* port interrupt enable */
_port->interrupt_enable();
/* ack all possibly pending interrupts */
_port->interrupt_ack();
_ctrl->hba_interrupt_ack();
/* retrieve block count */
_identify_device();
}
public:
Ahci_device(addr_t base, Io_mem_session_capability io_cap,
Pci::Connection &pci)
: Ahci_device_base(base, io_cap), _pci(pci), _pci_device(0) { }
/**
* Probe PCI-bus for AHCI and ATA-devices
*/
static Ahci_device *probe(Pci::Connection &pci)
{
Pci::Device_capability device_cap;
Ahci_device *device;
while ((device_cap = _scan_pci(pci, device_cap)).valid()) {
::Pci::Device_client * pci_device =
new(env()->heap()) ::Pci::Device_client(device_cap);
PINF("Found AHCI HBA (Vendor ID: %04x Device ID: %04x Class:"
" %08x)\n", pci_device->vendor_id(),
pci_device->device_id(), pci_device->class_code());
/* enable Bus Master, Memory Space, I/O Space access by dev */
pci_device->config_write(PCI_CFG_CMD_REG, 0x7,
::Pci::Device::ACCESS_16BIT);
/* read and map base address of AHCI controller */
Pci::Device::Resource resource = pci_device->resource(AHCI_BASE_ID);
Io_mem_connection io(resource.base(), resource.size());
addr_t addr = (addr_t)env()->rm_session()->attach(io.dataspace());
/* add possible resource offset */
addr += resource.base() & 0xfff;
if (verbose)
PDBG("resource base: %x virt: %lx", resource.base(), addr);
/* create and test device */
device = new(env()->heap()) Ahci_device(addr, io.cap(), pci);
if (device->_scan_ports()) {
io.on_destruction(Io_mem_connection::KEEP_OPEN);
/* read IRQ information */
unsigned long intr = pci_device->config_read(AHCI_INTR_OFF,
::Pci::Device::ACCESS_32BIT);
if (verbose) {
PDBG("Interrupt pin: %lu line: %lu", (intr >> 8) & 0xff, intr & 0xff);
unsigned char bus, dev, func;
pci_device->bus_address(&bus, &dev, &func);
PDBG("Bus address: %x:%02x.%u (0x%x)", bus, dev, func, (bus << 8) | ((dev & 0x1f) << 3) | (func & 0x7));
}
device->_irq = new(env()->heap()) Irq_session_client(pci_device->irq(0));
Genode::Signal_context_capability cap = device->_irq_rec.manage(&device->_irq_ctx);
device->_irq->sigh(cap);
device->_irq->ack_irq();
device->_pci_device = pci_device;
/* get device ready */
device->_init(pci_device);
return device;
}
destroy(env()->heap(), pci_device);
env()->rm_session()->detach(addr);
destroy(env()->heap(), device);
}
return 0;
}
Ram_dataspace_capability alloc_dma_buffer(size_t size)
{
/* transfer quota to pci driver, otherwise we get a exception */
char quota[32];
Genode::snprintf(quota, sizeof(quota), "ram_quota=%zd", size);
Genode::env()->parent()->upgrade(_pci.cap(), quota);
return _pci.alloc_dma_buffer(size);
}
void free_dma_buffer(Ram_dataspace_capability cap) {
return _pci.free_dma_buffer(cap); }
};
#endif /* _AHCI_DEVICE_H_ */

View File

@ -1,58 +0,0 @@
/*
* \brief AHCI driver
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
* \author Martin Stein <Martin.Stein@genode-labs.com>
* \date 2006-08-15
*/
/*
* Copyright (C) 2006-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _AHCI_DRIVER_H_
#define _AHCI_DRIVER_H_
/* Genode includes */
#include <pci_session/connection.h>
/* local includes */
#include <ahci_driver_base.h>
/**
* Helper class for AHCI driver to ensure specific member-initialization order
*/
class Ahci_pci_connection {
protected:
Pci::Connection _pci;
};
/**
* AHCI driver
*/
class Ahci_driver : public Ahci_pci_connection,
public Ahci_driver_base
{
public:
/**
* Constructor
*/
Ahci_driver() : Ahci_driver_base(nullptr) {
Ahci_device * device = Ahci_device::probe(_pci);
if (!device) {
PWRN("Could not find ahci device");
throw Root::Unavailable();
}
_device = device;
}
};
#endif /* _AHCI_DRIVER_H_ */

View File

@ -0,0 +1,137 @@
/**
* \brief Driver for PCI-bus platforms
* \author Sebastian Sumpf
* \date 2015-04-29
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <irq_session/connection.h>
#include <pci_session/connection.h>
#include <pci_device/client.h>
#include <util/volatile_object.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,
};
Pci::Connection pci;
Pci::Device_capability pci_device_cap;
Lazy_volatile_object<Pci::Device_client> pci_device;
Lazy_volatile_object<Irq_session_client> irq;
addr_t res_base;
size_t res_size;
X86_hba()
{
for (unsigned i = 0; i < 2; i++)
try {
if (!(pci_device_cap =
pci.next_device(pci_device_cap, AHCI_DEVICE, CLASS_MASK)).valid()) {
PERR("No AHCI controller found");
throw -1;
}
break;
} catch (Pci::Device::Quota_exceeded) {
Genode::env()->parent()->upgrade(pci.cap(), "ram_quota=4096");
}
/* construct pci client */
pci_device.construct(pci_device_cap);
PINF("AHCI found (vendor: %04x device: %04x class:"
" %08x)\n", pci_device->vendor_id(),
pci_device->device_id(), pci_device->class_code());
/* read base address of controller */
Pci::Device::Resource resource = pci_device->resource(AHCI_BASE_ID);
res_base = resource.base();
res_size = resource.size();
if (verbose)
PDBG("base: %lx size: %zx", res_base, res_size);
/* enable bus master */
uint16_t cmd = pci_device->config_read(PCI_CMD, Pci::Device::ACCESS_16BIT);
cmd |= 0x4;
pci_device->config_write(PCI_CMD, cmd, Pci::Device::ACCESS_16BIT);
irq.construct(pci_device->irq(0));
}
void disable_msi()
{
enum { PM_CAP_OFF = 0x34, MSI_CAP = 0x5, MSI_ENABLED = 0x1 };
uint8_t cap = pci_device->config_read(PM_CAP_OFF, ::Pci::Device::ACCESS_8BIT);
/* iterate through cap pointers */
for (uint16_t val = 0; cap; cap = val >> 8) {
val = pci_device->config_read(cap, ::Pci::Device::ACCESS_16BIT);
if ((val & 0xff) != MSI_CAP)
continue;
uint16_t msi = pci_device->config_read(cap + 2, ::Pci::Device::ACCESS_16BIT);
if (msi & MSI_ENABLED) {
pci_device->config_write(cap + 2, msi ^ MSI_CAP, ::Pci::Device::ACCESS_8BIT);
PINF("Disabled MSIs %x", msi);
}
}
}
/*******************
** Hba interface **
*******************/
Genode::addr_t base() const override { return res_base; }
Genode::size_t size() const override { return res_size; }
void sigh_irq(Signal_context_capability sigh) override
{
irq->sigh(sigh);
ack_irq();
}
void ack_irq() override { irq->ack_irq(); }
Ram_dataspace_capability
alloc_dma_buffer(Genode::size_t size) override
{
/* transfer quota to pci driver, otherwise we get a exception */
char quota[32];
snprintf(quota, sizeof(quota), "ram_quota=%zd", size);
env()->parent()->upgrade(pci.cap(), quota);
return pci.alloc_dma_buffer(size);
}
void free_dma_buffer(Genode::Ram_dataspace_capability ds)
{
pci.free_dma_buffer(ds);
}
};
Platform::Hba &Platform::init(Mmio::Delayer &)
{
static X86_hba h;
return h;
}

View File

@ -58,7 +58,7 @@ set config {
</provides>
<config><policy label="l4linux" uart="0" detect_size="yes"/></config>
</start>
<start name="ahci">
<start name="ahci_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Block"/></provides>
</start>
@ -109,7 +109,7 @@ set boot_modules {
kdb_uart_drv
l4linux
initrd.gz
ahci
ahci_drv
platform_drv }
lappend_if $interpose_part_blk boot_modules part_blk

View File

@ -168,8 +168,12 @@ append_if [expr !$use_usb] config {
append_if $use_block_sata config {
<start name="ahci" priority="-1">
<binary name="ahci_drv" />
<resource name="RAM" quantum="1M" />
<provides><service name="Block"/></provides>
<config>
<policy label="seoul" device="0" />
</config>
</start>}
append_if $use_genode_iso config {
@ -422,7 +426,7 @@ lappend_if [have_spec acpi] boot_modules acpi_drv
lappend_if [have_spec nova] boot_modules pci_device_pd
lappend_if [expr !$use_usb] boot_modules ps2_drv
lappend_if $use_usb boot_modules usb_drv
lappend_if $use_block_sata boot_modules ahci
lappend_if $use_block_sata boot_modules ahci_drv
lappend_if $use_nic_session boot_modules nic_drv
lappend_if $use_nic_bridge boot_modules nic_bridge
lappend_if $use_framebuffer boot_modules fb_drv