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

View File

@ -129,6 +129,25 @@ proc drivers_start_nodes { feature_arg } {
<service name="PCI"/> <service name="PCI"/>
<service name="IRQ" /> <service name="IRQ" />
</provides> </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> <route>
<service name="PCI"> <any-child /> </service> <service name="PCI"> <any-child /> </service>
<any-service> <parent/> <any-child /> </any-service> <any-service> <parent/> <any-child /> </any-service>
@ -179,6 +198,17 @@ proc drivers_start_nodes { feature_arg } {
<start name="pci_drv"> <start name="pci_drv">
<resource name="RAM" quantum="2M" constrain_phys="yes"/> <resource name="RAM" quantum="2M" constrain_phys="yes"/>
<provides><service name="PCI"/></provides> <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> </start>
} }

View File

@ -51,9 +51,12 @@ append config [qt5_start_nodes feature]
append_if $use_ahci_driver config { append_if $use_ahci_driver config {
<start name="ahci"> <start name="ahci">
<resource name="RAM" quantum="1M"/> <binary name="ahci_drv" />
<resource name="RAM" quantum="5M"/>
<provides> <service name="Block"/> </provides> <provides> <service name="Block"/> </provides>
<config ata="yes" /> <config>
<policy label="ffat_fs" device="0" />
</config>
</start>} </start>}
append_if $use_sd_card_driver config { 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 [have_spec linux] boot_modules ram_fs
lappend_if [expr ![have_spec linux]] boot_modules ffat_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_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 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 Behavior
-------- --------
The server implements Genode's new block-driver API ('os/include/block'), thus The driver supports x86 32/64 bit platforms and the Exynos5 SOC. If
exposing the block-session interface as front-end. AHCI depends on Genode's PCI more than one AHCI controller is present, the first one will be used.
driver as well as the timer server. For a usage example see: 'os/run/ahci.run'. 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 !<start name="ahci">
AHCI controller, scans the controller ports and uses the first non-ATAPI port ! <binary name="ahci_drv" />
where a device is present. ! <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 In the example above, a session request labeled with "test-ahci"
Fiasco.OC) we still lack support for ACPI parsing and thus for interrupts, gains access to device 0, while "bench" gains access to device 1.
leading to a non-working driver.

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 * \brief Block driver session creation
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com> * \author Sebastian Sumpf
* \date 2011-08-10 * \date 2015-09-29
*
* 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.
*/ */
/* /*
* 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 * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2. * under the terms of the GNU General Public License version 2.
*/ */
/* Genode includes */
#include <block/component.h> #include <block/component.h>
#include <os/config.h>
#include <os/server.h> #include <os/server.h>
#include <util/xml_node.h>
/* local includes */ #include "ahci.h"
#include <ahci_driver.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 struct Main
{ {
Server::Entrypoint &ep; Block::Root_multiple_clients root;
struct Factory : Block::Driver_factory
{
Block::Driver *create() {
static Ahci_driver driver;
return &driver; }
void destroy(Block::Driver *driver) { }
} factory;
Block::Root root;
Main(Server::Entrypoint &ep) Main(Server::Entrypoint &ep)
: ep(ep), root(ep, Genode::env()->heap(), factory) : root(ep, Genode::env()->heap())
{ {
printf("--- AHCI driver started ---\n"); PINF("--- Starting AHCI driver -> done right .-) --\n");
Ahci_driver::init(root);
Genode::env()->parent()->announce(ep.manage(root));
} }
}; };
/************
** Server **
************/
namespace Server { namespace Server {
char const *name() { return "ahci_ep"; } char const *name() { return "ahci_ep"; }
size_t stack_size() { return 2*1024*sizeof(long); } size_t stack_size() { return 2 * 1024 * sizeof(long); }
void construct(Entrypoint &ep) { static Main server(ep); } void construct(Entrypoint &ep) { static Main server(ep); }
} }

View File

@ -1,3 +1,3 @@
TARGET = ahci TARGET = ahci_drv
LIBS += ahci server SRC_CC = main.cc ahci.cc
SRC_CC += empty.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> </provides>
<config><policy label="l4linux" uart="0" detect_size="yes"/></config> <config><policy label="l4linux" uart="0" detect_size="yes"/></config>
</start> </start>
<start name="ahci"> <start name="ahci_drv">
<resource name="RAM" quantum="1M"/> <resource name="RAM" quantum="1M"/>
<provides><service name="Block"/></provides> <provides><service name="Block"/></provides>
</start> </start>
@ -109,7 +109,7 @@ set boot_modules {
kdb_uart_drv kdb_uart_drv
l4linux l4linux
initrd.gz initrd.gz
ahci ahci_drv
platform_drv } platform_drv }
lappend_if $interpose_part_blk boot_modules part_blk 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 { append_if $use_block_sata config {
<start name="ahci" priority="-1"> <start name="ahci" priority="-1">
<binary name="ahci_drv" />
<resource name="RAM" quantum="1M" /> <resource name="RAM" quantum="1M" />
<provides><service name="Block"/></provides> <provides><service name="Block"/></provides>
<config>
<policy label="seoul" device="0" />
</config>
</start>} </start>}
append_if $use_genode_iso config { 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 [have_spec nova] boot_modules pci_device_pd
lappend_if [expr !$use_usb] boot_modules ps2_drv lappend_if [expr !$use_usb] boot_modules ps2_drv
lappend_if $use_usb boot_modules usb_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_session boot_modules nic_drv
lappend_if $use_nic_bridge boot_modules nic_bridge lappend_if $use_nic_bridge boot_modules nic_bridge
lappend_if $use_framebuffer boot_modules fb_drv lappend_if $use_framebuffer boot_modules fb_drv