diff --git a/repos/os/run/ahci_block.run b/repos/os/run/ahci_block.run
index a92393e38a..3fbb15f9ca 100644
--- a/repos/os/run/ahci_block.run
+++ b/repos/os/run/ahci_block.run
@@ -62,9 +62,10 @@ append config {
-
+
-
+
+
@@ -80,6 +81,15 @@ append config {
+
+
+
+
+
+
+
+
+
}
install_config $config
@@ -96,8 +106,8 @@ build_boot_image $boot_modules
append qemu_args " -nographic -device ahci,id=ahci -boot d "
append qemu_args " -drive id=disk,file=bin/ext2.raw,format=raw,if=none -device ide-hd,drive=disk,bus=ahci.0 "
append qemu_args " -drive id=cd,file=[run_dir]/../ahci_block.iso,if=none,media=cdrom -device ide-cd,drive=cd,bus=ahci.1 "
-append qemu_args " -drive id=disk2,file=bin/ext2.raw,format=raw,if=none -device ide-hd,drive=disk2,bus=ahci.2 "
run_genode_until "Tests finished successfully!" 100
+run_genode_until "Tests finished successfully!" 100 [output_spawn_id]
exec rm -f bin/ext2.raw
diff --git a/repos/os/src/drivers/ahci/ahci.cc b/repos/os/src/drivers/ahci/ahci.cc
deleted file mode 100644
index 4be53f3333..0000000000
--- a/repos/os/src/drivers/ahci/ahci.cc
+++ /dev/null
@@ -1,278 +0,0 @@
-/**
- * \brief AHCI port controller handling
- * \author Sebastian Sumpf
- * \date 2015-04-29
- */
-
-/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
- *
- * This file is part of the Genode OS framework, which is distributed
- * under the terms of the GNU Affero General Public License version 3.
- */
-
-
-/* Genode includes */
-#include
-
-/* local includes */
-#include
-#include
-
-
-struct Ahci
-{
- Genode::Env &env;
- Genode::Allocator &alloc;
-
- /* read device signature */
- enum Signature {
- ATA_SIG = 0x101,
- ATAPI_SIG = 0xeb140101,
- ATAPI_SIG_QEMU = 0xeb140000, /* will be fixed in Qemu */
- };
-
- struct Timer_delayer : Mmio::Delayer, Timer::Connection
- {
- Timer_delayer(Genode::Env &env)
- : Timer::Connection(env) { }
-
- void usleep(uint64_t us) override { Timer::Connection::usleep(us); }
- } _delayer { env };
-
- Ahci_root &root;
- Platform::Hba &platform_hba = Platform::init(env, _delayer);
- Hba hba { env, platform_hba, _delayer };
-
- enum { MAX_PORTS = 32 };
- Port_driver *ports[MAX_PORTS];
- bool port_claimed[MAX_PORTS];
-
- Signal_handler irq;
- unsigned ready_count = 0;
- bool enable_atapi;
-
- Signal_context_capability device_identified;
-
- Ahci(Genode::Env &env, Genode::Allocator &alloc,
- Ahci_root &root, bool support_atapi,
- Genode::Signal_context_capability device_identified)
- :
- env(env), alloc(alloc),
- root(root), irq(root.entrypoint(), *this, &Ahci::handle_irq),
- enable_atapi(support_atapi),
- device_identified(device_identified)
- {
- info();
-
- /* register irq handler */
- platform_hba.sigh_irq(irq);
-
- /* initialize HBA (IRQs, memory) */
- hba.init();
-
- /* search for devices */
- scan_ports(env.rm(), env.ram());
- }
-
- /**
- * Forward IRQs to ports
- */
- void handle_irq()
- {
- unsigned port_list = hba.read();
- while (port_list) {
- unsigned port = log2(port_list);
- port_list &= ~(1U << port);
-
- ports[port]->handle_irq();
- }
-
- /* clear status register */
- hba.ack_irq();
-
- /* ack at interrupt controller */
- platform_hba.ack_irq();
- }
-
- /*
- * Least significant bit
- */
- unsigned lsb(unsigned bits) const
- {
- for (unsigned i = 0; i < 32; i++)
- if (bits & (1u << i)) {
- return i;
- }
-
- return 0;
- }
-
- void info()
- {
- using Genode::log;
-
- log("version: "
- "major=", Genode::Hex(hba.read()), " "
- "minor=", Genode::Hex(hba.read()));
- log("command slots: ", hba.command_slots());
- log("native command queuing: ", hba.ncq() ? "yes" : "no");
- log("64-bit support: ", hba.supports_64bit() ? "yes" : "no");
- }
-
- void scan_ports(Genode::Region_map &rm, Genode::Ram_allocator &ram)
- {
- log("number of ports: ", hba.port_count(), " pi: ",
- Hex(hba.read()));
-
- unsigned available = hba.read();
- for (unsigned i = 0; i < hba.port_count(); i++) {
-
- /* check if port is implemented */
- if (!available) break;
- unsigned index = lsb(available);
- available ^= (1u << index);
-
- bool enabled = false;
-
- switch (Port_base(index, hba).read()) {
- case ATA_SIG:
- try {
- ports[index] = new (&alloc)
- Ata_driver(alloc, ram, root, ready_count, rm, hba,
- platform_hba, index, device_identified);
- enabled = true;
- } catch (...) { }
-
- log("\t\t#", index, ":", enabled ? " ATA" : " off (ATA)");
- break;
-
- case ATAPI_SIG:
- case ATAPI_SIG_QEMU:
- if (enable_atapi)
- try {
- ports[index] = new (&alloc)
- Atapi_driver(ram, root, ready_count, rm, hba,
- platform_hba, index);
- enabled = true;
- } catch (...) { }
-
- log("\t\t#", index, ":", enabled ? " ATAPI" : " off (ATAPI)");
- break;
-
- default:
- log("\t\t#", index, ": off (unknown device signature)");
- }
- }
- };
-
- Block::Driver *claim_port(unsigned port_num)
- {
- if (!avail(port_num))
- throw -1;
-
- port_claimed[port_num] = true;
- return ports[port_num];
- }
-
- void free_port(unsigned port_num)
- {
- port_claimed[port_num] = false;
- }
-
- bool avail(unsigned port_num)
- {
- return port_num < MAX_PORTS && ports[port_num] && !port_claimed[port_num] &&
- ports[port_num]->ready();
- }
-
- Port_driver * port(unsigned num)
- {
- return num < MAX_PORTS ? ports[num] : nullptr;
- }
-
- long device_number(const char *model_num, const char *serial_num)
- {
- for (long port_num = 0; port_num < MAX_PORTS; port_num++) {
- Ata_driver* drv = dynamic_cast(ports[port_num]);
- if (!drv)
- continue;
-
- if (*drv->model == model_num && *drv->serial == serial_num)
- return port_num;
- }
- return -1;
- }
-
- private:
-
- /*
- * Noncopyable
- */
- Ahci(Ahci const &);
- Ahci &operator = (Ahci const &);
-};
-
-
-static Ahci *sata_ahci(Ahci *ahci = 0)
-{
- static Ahci *a = ahci;
- return a;
-}
-
-
-void Ahci_driver::init(Genode::Env &env, Genode::Allocator &alloc,
- Ahci_root &root, bool support_atapi,
- Genode::Signal_context_capability device_identified)
-{
- static Ahci ahci(env, alloc, root, support_atapi, device_identified);
- sata_ahci(&ahci);
-}
-
-
-Block::Driver *Ahci_driver::claim_port(long device_num)
-{
- return sata_ahci()->claim_port(device_num);
-}
-
-
-void Ahci_driver::free_port(long device_num)
-{
- sata_ahci()->free_port(device_num);
-}
-
-
-bool Ahci_driver::avail(long device_num)
-{
- return sata_ahci()->avail(device_num);
-}
-
-
-long Ahci_driver::device_number(char const *model_num, char const *serial_num)
-{
- return sata_ahci()->device_number(model_num, serial_num);
-}
-
-
-void Ahci_driver::report_ports(Genode::Reporter &reporter)
-{
- Genode::Reporter::Xml_generator xml(reporter, [&] () {
- for (unsigned i = 0; i < Ahci::MAX_PORTS; ++i) {
- Port_driver *port = sata_ahci()->port(i);
- if (!port || !port->ready()) continue;
-
- Ata_driver *ata = dynamic_cast(port);
-
- xml.node("port", [&] () {
- xml.attribute("num", i);
- xml.attribute("type", ata ? "ATA" : "ATAPI");
- if (ata) {
- xml.attribute("block_count", ata->block_count());
- xml.attribute("block_size", ata->block_size());
- xml.attribute("model", ata->model->cstring());
- xml.attribute("serial", ata->serial->cstring());
- }
- });
- }
- });
-}
diff --git a/repos/os/src/drivers/ahci/ahci.h b/repos/os/src/drivers/ahci/ahci.h
index d72c2e4e34..31b87541ab 100644
--- a/repos/os/src/drivers/ahci/ahci.h
+++ b/repos/os/src/drivers/ahci/ahci.h
@@ -1,4 +1,5 @@
/**
+ *
* \brief Generic AHCI controller definitions
* \author Sebasitan Sumpf
* \date 2015-04-29
@@ -11,77 +12,73 @@
* under the terms of the GNU Affero General Public License version 3.
*/
-#ifndef _INCLUDE__AHCI_H_
-#define _INCLUDE__AHCI_H_
+#ifndef _AHCI__AHCI_H_
+#define _AHCI__AHCI_H_
-#include
+#include
#include
#include
#include
#include
+#include
+
static bool constexpr verbose = false;
-namespace Platform {
+namespace Ahci {
+ struct Missing_controller : Exception { };
+
+ class Platform;
+ struct Protocol;
+ struct Port;
+ struct Port_base;
struct Hba;
- Hba &init(Genode::Env &env, Genode::Mmio::Delayer &delayer);
-};
-
-struct Ahci_root : Genode::Interface
-{
- virtual Genode::Entrypoint &entrypoint() = 0;
- virtual void announce() = 0;
-};
-
-
-namespace Ahci_driver {
-
- void init(Genode::Env &env, Genode::Allocator &alloc, Ahci_root &ep,
- bool support_atapi, Genode::Signal_context_capability device_identified);
-
- bool avail(long device_num);
- long device_number(char const *model_num, char const *serial_num);
-
- Block::Driver *claim_port(long device_num);
- void free_port(long device_num);
- void report_ports(Genode::Reporter &reporter);
-
- struct Missing_controller { };
+ using Response = Block::Request_stream::Response;
+ using block_number_t = Block::block_number_t;
+ using block_count_t = Block::block_count_t;
}
-
-struct Platform::Hba : Genode::Interface
+class Ahci::Platform
{
- /**
- * Return base address and size of HBA device registers
- */
- virtual Genode::addr_t base() const = 0;
- virtual Genode::size_t size() const = 0;
+ private :
- /**
- * Register interrupt signal context
- */
- virtual void sigh_irq(Genode::Signal_context_capability sigh) = 0;
- virtual void ack_irq() = 0;
+ Data _data;
- /**
- * 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;
+ protected:
+
+ /**
+ * Return base address and size of HBA device registers
+ */
+ addr_t _mmio_base() const;
+
+ public:
+
+ Platform(Env &env) : _data(env) { };
+
+ /**
+ * Register interrupt signal context
+ */
+ void sigh_irq(Signal_context_capability sigh);
+ void ack_irq();
+
+ /**
+ * DMA
+ */
+ Ram_dataspace_capability alloc_dma_buffer(size_t size);
+ void free_dma_buffer(Ram_dataspace_capability ds);
};
-
/**
* HBA definitions
*/
-struct Hba : Genode::Attached_mmio
+struct Ahci::Hba : Ahci::Platform,
+ Mmio
{
Mmio::Delayer &_delayer;
- Hba(Genode::Env &env, Platform::Hba &hba, Mmio::Delayer &delayer)
- : Attached_mmio(env, hba.base(), hba.size()), _delayer(delayer) { }
+ Hba(Env &env, Mmio::Delayer &delayer)
+ : Platform(env), Mmio(_mmio_base()), _delayer(delayer) { }
/**
* Host capabilites
@@ -115,7 +112,11 @@ struct Hba : Genode::Attached_mmio
*/
struct Is : Register<0x8, 32> { };
- void ack_irq() { write(read()); }
+ void ack_irq()
+ {
+ write(read());
+ Platform::ack_irq();
+ }
/**
* Ports implemented
@@ -146,333 +147,400 @@ struct Hba : Genode::Attached_mmio
};
-struct Device_fis : Genode::Mmio
-{
- struct Status : Register<0x2, 8>
+/***********************************
+ ** AHCI commands and descriptors **
+ ***********************************/
+
+namespace Ahci {
+
+ struct Device_fis : Mmio
{
- /* ATAPI bits */
- struct Device_ready : Bitfield<6, 1> { };
- };
- struct Error : Register<0x3, 8> { };
+ 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 { };
- struct Device : Register<0x7, 8>
- {
- struct Lba : Bitfield<6, 1> { }; /* enable LBA mode */
+ Device_fis(addr_t recv_base)
+ : Mmio(recv_base + 0x40) { }
};
- /* 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 { };
- struct Lba48 : Genode::Bitset_3 { };
- struct Lba : Genode::Bitset_2 { }; /* LBA 0 - 47 */
- /* big endian */
- struct Sector0_7 : Register<0xc, 8, 1>
+ struct Command_fis : Mmio
{
- struct Tag : Bitfield<3, 5> { };
- };
- struct Sector8_15 : Register<0xd, 8> { };
- struct Sector : Genode::Bitset_2 { }; /* sector count */
+ 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> { };
- Command_fis(Genode::addr_t base)
- : Mmio(base)
- {
- clear();
+ /* big-endian use byte access */
+ struct Lba0_7 : Register<0x4, 8> { };
+ struct Lba8_15 : Register<0x5, 8> { };
+ struct Lba16_23 : Register<0x6, 8> { };
+ struct Lba24 : Bitset_3 { };
+ struct Device : Register<0x7, 8>
+ {
+ struct Lba : Bitfield<6, 1> { }; /* enable LBA mode */
+ };
- enum { HOST_TO_DEVICE = 0x27 };
- write(HOST_TO_DEVICE);
- }
+ /* big endian */
+ struct Lba24_31 : Register<0x8, 8> { };
+ struct Lba32_39 : Register<0x9, 8> { };
+ struct Lba40_47 : Register<0xa, 8> { };
+ struct Features8_15 : Register<0xb, 8> { };
+ struct Features : Bitset_2 { };
+ struct Lba48 : Bitset_3 { };
+ struct Lba : Bitset_2 { }; /* LBA 0 - 47 */
- static constexpr Genode::size_t size() { return 0x14; }
- void clear() { Genode::memset((void *)base(), 0, size()); }
+ /* big endian */
+ struct Sector0_7 : Register<0xc, 8, 1>
+ {
+ struct Tag : Bitfield<3, 5> { };
+ };
+ struct Sector8_15 : Register<0xd, 8> { };
+ struct Sector : Bitset_2 { }; /* sector count */
+
+ Command_fis(addr_t base)
+ : Mmio(base)
+ {
+ clear();
+
+ enum { HOST_TO_DEVICE = 0x27 };
+ write(HOST_TO_DEVICE);
+ }
+
+ static constexpr size_t size() { return 0x14; }
+ void clear() { memset((void *)base(), 0, size()); }
- /************************
- ** ATA spec commands **
- ************************/
+ /************************
+ ** ATA spec commands **
+ ************************/
- void identify_device()
- {
- write(1);
- write(0);
- write(0xec);
- }
+ void identify_device()
+ {
+ write(1);
+ write(0);
+ write(0xec);
+ }
- void dma_ext(bool read, Block::sector_t block_number, Genode::size_t block_count)
- {
- write(1);
- write(1);
- /* read_dma_ext : write_dma_ext */
- write(read ? 0x25 : 0x35);
- write(block_number);
- write(block_count);
- }
+ void dma_ext(bool read, block_number_t block_number, block_count_t block_count)
+ {
+ write(1);
+ write(1);
+ /* read_dma_ext : write_dma_ext */
+ write(read ? 0x25 : 0x35);
+ write(block_number);
+ write(block_count);
+ }
- void fpdma(bool read, Block::sector_t block_number, Genode::size_t block_count,
- unsigned slot)
- {
- write(1);
- write(1);
- /* read_fpdma : write_fpdma */
- write(read ? 0x60 : 0x61);
- write(block_number);
- write(block_count);
- write(slot);
- }
+ void fpdma(bool read, block_number_t block_number, block_count_t block_count,
+ unsigned slot)
+ {
+ write(1);
+ write(1);
+ /* read_fpdma : write_fpdma */
+ write(read ? 0x60 : 0x61);
+ write(block_number);
+ write(block_count);
+ write(slot);
+ }
- void atapi()
- {
- write(1);
- /* packet command */
- write(0xa0);
- }
-};
+ void atapi()
+ {
+ write(1);
+ /* packet command */
+ write(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 */
+ /*
+ * Sets byte count limit for PIO transfers
+ */
+ void byte_count(uint16_t bytes)
+ {
+ write(bytes & 0xff);
+ write(bytes >> 8);
+ }
};
- 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)
+ /**
+ * AHCI command list structure header
+ */
+ struct Command_header : Mmio
{
- Genode::uint64_t addr = base_phys;
- write(addr);
- write(addr >> 32);
- write(1);
- write(Command_fis::size() / sizeof(unsigned));
- }
+ 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 */
+ };
- void clear_byte_count()
- {
- write(0);
- }
+ 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) */
- void atapi_command()
- {
- write(1);
- }
+ Command_header(addr_t base) : Mmio(base) { }
- static constexpr Genode::size_t size() { return 0x20; }
-};
+ void cmd_table_base(addr_t base_phys)
+ {
+ uint64_t addr = base_phys;
+ write(addr);
+ write(addr >> 32);
+ write(1);
+ write(Command_fis::size() / sizeof(unsigned));
+ }
+ void clear_byte_count()
+ {
+ write(0);
+ }
-/**
- * ATAPI packet 12 or 16 bytes
- */
-struct Atapi_command : Genode::Mmio
-{
- struct Command : Register<0, 8> { };
+ void atapi_command()
+ {
+ write(1);
+ }
- /* 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 { };
- struct Lba : Genode::Bitset_2 { };
-
- /* sector count big endian */
- struct Sector8_15 : Register<0x8, 8> { };
- struct Sector0_7 : Register<0x9, 8> { };
- struct Sector : Genode::Bitset_2 { };
-
-
- Atapi_command(Genode::addr_t base) : Mmio(base)
- {
- Genode::memset((void *)base, 0, 16);
- }
-
- void read_capacity()
- {
- write(0x25);
- }
-
- void test_unit_ready()
- {
- write(0x0);
- }
-
- void read_sense()
- {
- write(0x3);
- write(18);
- }
-
- void read10(Block::sector_t block_number, Genode::size_t block_count)
- {
- write(0x28);
- write(block_number);
- write(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 */
+ static constexpr size_t size() { return 0x20; }
};
- Prdt(Genode::addr_t base, Genode::addr_t phys, Genode::size_t bytes)
- : Mmio(base)
+
+ /**
+ * ATAPI packet 12 or 16 bytes
+ */
+ struct Atapi_command : Mmio
{
- Genode::uint64_t addr = phys;
- write(addr);
- write(addr >> 32);
- write(bytes > 0 ? bytes - 1 : 0);
- }
+ struct Command : Register<0, 8> { };
- static constexpr Genode::size_t size() { return 0x10; }
-};
+ /* LBA32 big endian */
+ struct Lba24_31 : Register<0x2, 8> { };
+ struct Lba16_23 : Register<0x3, 8> { };
+ struct Lba8_15 : Register<0x4, 8> { };
+ struct Lba0_7 : Register<0x5, 8> { };
+ struct Lba24 : Bitset_3 { };
+ struct Lba : Bitset_2 { };
+
+ /* sector count big endian */
+ struct Sector8_15 : Register<0x8, 8> { };
+ struct Sector0_7 : Register<0x9, 8> { };
+ struct Sector : Bitset_2 { };
-struct Command_table
-{
- Command_fis fis;
- Atapi_command atapi_cmd;
+ Atapi_command(addr_t base) : Mmio(base)
+ {
+ memset((void *)base, 0, 16);
+ }
- /* in Genode we only need one PRD (for one packet) */
- Prdt prdt;
+ void read_capacity()
+ {
+ write(0x25);
+ }
- 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)
- { }
+ void test_unit_ready()
+ {
+ write(0x0);
+ }
- static constexpr Genode::size_t size() { return 0x100; }
-};
+ void read_sense()
+ {
+ write(0x3);
+ write(18);
+ }
+ void read10(block_number_t block_number, block_count_t block_count)
+ {
+ write(0x28);
+ write(block_number);
+ write(block_count);
+ }
+
+ void start_unit()
+ {
+ write(0x1b);
+ }
+ };
+
+
+ /**
+ * Physical region descritpor table
+ */
+ struct Prdt : Mmio
+ {
+ struct Dba : Register<0x0, 32> { }; /* data base address */
+ struct Dbau : Register<0x4, 32> { }; /* data base address upper 32 bits */
+
+ struct Bits : Register<0xc, 32>
+ {
+ struct Dbc : Bitfield<0, 22> { }; /* data byte count */
+ struct Irq : Bitfield<31,1> { }; /* interrupt completion */
+ };
+
+ Prdt(addr_t base, addr_t phys, size_t bytes)
+ : Mmio(base)
+ {
+ uint64_t addr = phys;
+ write(addr);
+ write(addr >> 32);
+ write(bytes > 0 ? bytes - 1 : 0);
+ }
+
+ static constexpr size_t size() { return 0x10; }
+ };
+
+
+ struct Command_table
+ {
+ Command_fis fis;
+ Atapi_command atapi_cmd;
+
+ /* in Genode we only need one PRD (for one packet) */
+ Prdt prdt;
+
+ Command_table(addr_t base,
+ addr_t phys,
+ size_t bytes = 0)
+ : fis(base), atapi_cmd(base + 0x40),
+ prdt(base + 0x80, phys, bytes)
+ { }
+
+ static constexpr size_t size() { return 0x100; }
+ };
+} /* namespace Ahci */
/**
* Minimalistic AHCI port structure to merely detect device signature
*/
-struct Port_base : Genode::Mmio
+struct Ahci::Port_base : Mmio
{
+ /* device signature */
+ enum Signature {
+ ATA_SIG = 0x101,
+ ATAPI_SIG = 0xeb140101,
+ ATAPI_SIG_QEMU = 0xeb140000, /* will be fixed in Qemu */
+ };
+
+ unsigned index { };
+ Hba &hba;
+
/**
* Port signature
*/
struct Sig : Register<0x24, 32> { };
- static constexpr Genode::addr_t offset() { return 0x100; }
- static constexpr Genode::size_t size() { return 0x80; }
+ static constexpr addr_t offset() { return 0x100; }
+ static constexpr size_t size() { return 0x80; }
- Port_base(unsigned number, Hba &hba)
- : Mmio(hba.base() + offset() + (number * size())) { }
+ Port_base(unsigned index, Hba &hba)
+ : Mmio(hba.base() + offset() + (index * size())),
+ index(index), hba(hba) { }
+
+ bool implemented() const
+ {
+ return hba.read() & (1u << index);
+ }
+
+ bool ata() const { return read() == ATA_SIG; }
+
+ bool atapi() const
+ {
+ unsigned sig = read();
+ return sig == ATAPI_SIG || sig == ATAPI_SIG_QEMU;
+ }
};
+struct Ahci::Protocol : Interface
+{
+ virtual unsigned init(Port &port) = 0;
+ virtual Block::Session::Info info() const = 0;
+ virtual void handle_irq(Port &port) = 0;
+
+ virtual Response submit(Port &port, Block::Request const request) = 0;
+ virtual Block::Request completed(Port &port) = 0;
+
+ virtual void writeable(bool rw) = 0;
+};
+
/**
* AHCI port
*/
-struct Port : private Port_base
+struct Ahci::Port : private Port_base
{
using Port_base::write;
using Port_base::read;
+ using Port_base::wait_for_any;
+ using Port_base::wait_for;
+ using Port_base::Register_set::Polling_timeout;
+ using Port_base::index;
+ using Port_base::hba;
- struct Not_ready : Genode::Exception { };
+ struct Not_ready : Exception { };
- Genode::Region_map &rm;
- Hba &hba;
- Platform::Hba &platform_hba;
- unsigned cmd_slots = hba.command_slots();
+ Protocol &protocol;
+ Region_map &rm;
+ unsigned cmd_slots = hba.command_slots();
- Genode::Ram_dataspace_capability device_ds { };
- Genode::Ram_dataspace_capability cmd_ds { };
- Genode::Ram_dataspace_capability device_info_ds { };
+ Ram_dataspace_capability device_ds { };
+ Ram_dataspace_capability cmd_ds { };
+ Ram_dataspace_capability device_info_ds { };
- Genode::addr_t cmd_list = 0;
- Genode::addr_t fis_base = 0;
- Genode::addr_t cmd_table = 0;
- Genode::addr_t device_info = 0;
+ addr_t cmd_list = 0;
+ addr_t fis_base = 0;
+ addr_t cmd_table = 0;
+ addr_t device_info = 0;
+ addr_t dma_base = 0; /* physical address of DMA memory */
- enum State {
- NONE,
- STATUS,
- TEST_READY,
- IDENTIFY,
- READY,
- };
-
- State state = NONE;
-
- Port(Genode::Region_map &rm, Hba &hba, Platform::Hba &platform_hba,
- unsigned number)
+ Port(Protocol &protocol, Region_map &rm, Hba &hba,
+ unsigned index)
:
- Port_base(number, hba), rm(rm), hba(hba),
- platform_hba(platform_hba)
+ Port_base(index, hba),
+ protocol(protocol), rm(rm)
{
reset();
if (!enable())
throw 1;
+
stop();
+
wait_for(hba.delayer(), Cmd::Cr::Equal(0));
+
+ init();
+
+ /*
+ * Init protocol and determine actual number of command slots of device
+ */
+ try {
+ unsigned device_slots = protocol.init(*this);
+ cmd_slots = min(device_slots, cmd_slots);
+ } catch (Polling_timeout) {
+ /* ack any pending IRQ from failed device initialization */
+ ack_irq();
+ throw;
+ }
}
virtual ~Port()
{
if (device_ds.valid()) {
rm.detach((void *)cmd_list);
- platform_hba.free_dma_buffer(device_ds);
+ hba.free_dma_buffer(device_ds);
}
if (cmd_ds.valid()) {
rm.detach((void *)cmd_table);
- platform_hba.free_dma_buffer(cmd_ds);
+ hba.free_dma_buffer(cmd_ds);
}
if (device_info_ds.valid()) {
rm.detach((void*)device_info);
- platform_hba.free_dma_buffer(device_info_ds);
+ hba.free_dma_buffer(device_info_ds);
}
}
@@ -486,9 +554,9 @@ struct Port : private Port_base
*/
struct Clbu : Register<0x4, 32> { };
- void command_list_base(Genode::addr_t phys)
+ void command_list_base(addr_t phys)
{
- Genode::uint64_t addr = phys;
+ uint64_t addr = phys;
write(addr);
write(addr >> 32);
}
@@ -503,9 +571,9 @@ struct Port : private Port_base
*/
struct Fbu : Register<0xc, 32> { };
- void fis_rcv_base(Genode::addr_t phys)
+ void fis_rcv_base(addr_t phys)
{
- Genode::uint64_t addr = phys;
+ uint64_t addr = phys;
write(addr);
write(addr >> 32);
}
@@ -589,6 +657,7 @@ struct Port : private Port_base
struct Sud : Bitfield<1, 1> { }; /* spin-up device */
struct Pod : Bitfield<2, 1> { }; /* power-up device */
struct Fre : Bitfield<4, 1> { }; /* FIS receive enable */
+ struct Fr : Bitfield<14, 1> { }; /* FIS receive running */
struct Cr : Bitfield<15, 1> { }; /* command list running */
struct Atapi : Bitfield<24, 1> { };
struct Icc : Bitfield<28, 4> { }; /* interface communication control */
@@ -602,14 +671,14 @@ struct Port : private Port_base
try {
wait_for(hba.delayer(), Tfd::Sts_bsy::Equal(0));
} catch (Polling_timeout) {
- Genode::error("HBA busy unable to start command processing.");
+ error("HBA busy unable to start command processing.");
return;
}
try {
wait_for(hba.delayer(), Tfd::Sts_drq::Equal(0));
} catch (Polling_timeout) {
- Genode::error("HBA in DRQ unable to start command processing.");
+ error("HBA in DRQ unable to start command processing.");
return;
}
@@ -672,7 +741,7 @@ struct Port : private Port_base
/* try to wake up device */
write(Ssts::Ipm::ACTIVE);
- Genode::retry(
+ retry(
[&] {
if ((Ssts::Dec::get(status) != Ssts::Dec::ESTABLISHED) ||
!(Ssts::Ipm::get(status) & Ssts::Ipm::ACTIVE))
@@ -700,7 +769,7 @@ struct Port : private Port_base
void reset()
{
if (read())
- Genode::warning("CMD.ST bit set during device reset --> unknown behavior");
+ warning("CMD.ST bit set during device reset --> unknown behavior");
write(1);
hba.delayer().usleep(1000);
@@ -709,7 +778,7 @@ struct Port : private Port_base
try {
wait_for(hba.delayer(), Ssts::Dec::Equal(Ssts::Dec::ESTABLISHED));
} catch (Polling_timeout) {
- Genode::warning("Port reset failed");
+ warning("Port reset failed");
}
}
@@ -728,14 +797,14 @@ struct Port : private Port_base
void clear_serr() { write(read()); }
/**
- * Serial ATA active
+ * Serial ATA active (strict write)
*/
- struct Sact : Register<0x34, 32> { };
+ struct Sact : Register<0x34, 32, true> { };
/**
- * Command issue
+ * Command issue (strict write)
*/
- struct Ci : Register<0x38, 32> { };
+ struct Ci : Register<0x38, 32, true> { };
void init()
{
@@ -767,25 +836,29 @@ struct Port : private Port_base
void setup_memory()
{
- using Genode::addr_t;
- using Genode::size_t;
-
- device_ds = platform_hba.alloc_dma_buffer(0x1000);
+ device_ds = hba.alloc_dma_buffer(0x1000);
/* command list 1K */
- addr_t phys = Genode::Dataspace_client(device_ds).phys_addr();
+ addr_t phys = Dataspace_client(device_ds).phys_addr();
cmd_list = (addr_t)rm.attach(device_ds);
command_list_base(phys);
/* receive FIS base 256 byte */
fis_base = cmd_list + 1024;
+
+ /*
+ * Set fis receive base, clear Fre (FIS receive) before and wait for FR
+ * (FIS receive running) to clear
+ */
+ write(0);
+ wait_for(hba.delayer(), Cmd::Fr::Equal(0));
fis_rcv_base(phys + 1024);
/* command table */
- size_t cmd_size = Genode::align_addr(cmd_slots * Command_table::size(), 12);
- cmd_ds = platform_hba.alloc_dma_buffer(cmd_size);
+ size_t cmd_size = align_addr(cmd_slots * Command_table::size(), 12);
+ cmd_ds = hba.alloc_dma_buffer(cmd_size);
cmd_table = (addr_t)rm.attach(cmd_ds);
- phys = (addr_t)Genode::Dataspace_client(cmd_ds).phys_addr();
+ phys = (addr_t)Dataspace_client(cmd_ds).phys_addr();
/* set command table addresses in command list */
for (unsigned i = 0; i < cmd_slots; i++) {
@@ -794,16 +867,16 @@ struct Port : private Port_base
}
/* dataspace for device info */
- device_info_ds = platform_hba.alloc_dma_buffer(0x1000);
+ device_info_ds = hba.alloc_dma_buffer(0x1000);
device_info = rm.attach(device_info_ds);
}
- Genode::addr_t command_table_addr(unsigned slot)
+ addr_t command_table_addr(unsigned slot)
{
return cmd_table + (slot * Command_table::size());
};
- Genode::addr_t command_header_addr(unsigned slot)
+ addr_t command_header_addr(unsigned slot)
{
return cmd_list + (slot * Command_header::size());
}
@@ -814,68 +887,62 @@ struct Port : private Port_base
write(1U << slot);
}
- bool ready() { return state == READY; }
-};
-
-
-struct Port_driver : Port, Block::Driver
-{
- Ahci_root &root;
- unsigned &sem;
-
- Port_driver(Genode::Ram_allocator &ram,
- Ahci_root &root,
- unsigned &sem,
- Genode::Region_map &rm,
- Hba &hba,
- Platform::Hba &platform_hba,
- unsigned number)
- : Port(rm, hba, platform_hba, number), Block::Driver(ram), root(root),
- sem(sem) { sem++; }
-
- virtual void handle_irq() = 0;
-
- virtual Genode::size_t block_size() const = 0;
- virtual Block::sector_t block_count() const = 0;
-
- void state_change()
+ bool sanity_check(Block::Request const &request)
{
- if (--sem) return;
+ Block::Session::Info const i = info();
- /* announce service */
- root.announce();
- }
-
- void sanity_check(Block::sector_t block_number, Genode::size_t count)
- {
/* max. PRDT size is 4MB */
- if (count * block_size() > 4 * 1024 * 1024) {
- Genode::error("error: maximum supported packet size is 4MB");
- throw Io_error();
+ if (request.operation.count * i.block_size > 4 * 1024 * 1024) {
+ error("error: maximum supported packet size is 4MB");
+ return false;
}
/* sanity check */
- if (block_number + count > block_count()) {
- Genode::error("error: requested blocks are outside of device");
- throw Io_error();
+ if (request.operation.count + request.operation.block_number > i.block_count) {
+ error("error: requested blocks are outside of device");
+ return false;
}
+
+ return true;
}
-
- /*******************
- ** Block::Driver **
- *******************/
-
- Genode::Ram_dataspace_capability
- alloc_dma_buffer(Genode::size_t size) override
+ Ram_dataspace_capability alloc_buffer(size_t size)
{
- return platform_hba.alloc_dma_buffer(size);
+ if (dma_base) return Ram_dataspace_capability();
+
+ Ram_dataspace_capability dma = hba.alloc_dma_buffer(size);
+ dma_base = Dataspace_client(dma).phys_addr();
+ return dma;
}
- void free_dma_buffer(Genode::Ram_dataspace_capability c) override
+ void free_buffer(Ram_dataspace_capability ds)
{
- platform_hba.free_dma_buffer(c);
+ dma_base = 0;
+ hba.free_dma_buffer(ds);
}
+
+ /**********************
+ ** Protocol wrapper **
+ **********************/
+
+
+ Block::Session::Info info() const { return protocol.info(); }
+ void handle_irq() { protocol.handle_irq(*this); }
+
+ Response submit(Block::Request const request) {
+ return protocol.submit(*this, request); }
+
+ template
+ void for_one_completed_request(FN const &fn)
+ {
+ Block::Request request = protocol.completed(*this);
+ if (!request.operation.valid()) return;
+
+ request.success = true;
+ fn(request);
+ }
+
+ void writeable(bool rw) { protocol.writeable(rw); }
};
-#endif /* _INCLUDE__AHCI_H_ */
+#endif /* _AHCI__AHCI_H_ */
diff --git a/repos/os/src/drivers/ahci/ata_driver.h b/repos/os/src/drivers/ahci/ata_driver.h
deleted file mode 100644
index 538ee9e4b8..0000000000
--- a/repos/os/src/drivers/ahci/ata_driver.h
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * \brief AHCI-port driver for ATA devices
- * \author Sebastian Sumpf
- * \date 2015-04-29
- */
-
-/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
- *
- * This file is part of the Genode OS framework, which is distributed
- * under the terms of the GNU Affero General Public License version 3.
- */
-
-#ifndef _ATA_DRIVER_H_
-#define _ATA_DRIVER_H_
-
-#include
-#include "ahci.h"
-
-using namespace Genode;
-
-
-/**
- * Return data of 'identify_device' ATA command
- */
-struct Identity : Genode::Mmio
-{
- Identity(Genode::addr_t base) : Mmio(base) { }
-
- struct Serial_number : Register_array<0x14, 8, 20, 8> { };
- struct Model_number : Register_array<0x36, 8, 40, 8> { };
-
- struct Queue_depth : Register<0x96, 16>
- {
- struct Max_depth : Bitfield<0, 5> { };
- };
-
- struct Sata_caps : Register<0x98, 16>
- {
- struct Ncq_support : Bitfield<8, 1> { };
- };
-
- struct Sector_count : Register<0xc8, 64> { };
-
- struct Logical_block : Register<0xd4, 16>
- {
- struct Per_physical : Bitfield<0, 3> { }; /* 2^X logical per physical */
- struct Longer_512 : Bitfield<12, 1> { };
- struct Multiple : Bitfield<13, 1> { }; /* multiple logical blocks per physical */
- };
-
- struct Logical_words : Register<0xea, 32> { }; /* words (16 bit) per logical block */
-
- struct Alignment : Register<0x1a2, 16>
- {
- struct Logical_offset : Bitfield<0, 14> { }; /* offset first logical block in physical */
- };
-
- void info()
- {
- using Genode::log;
-
- log(" queue depth: ", read() + 1, " "
- "ncq: ", read());
- log(" numer of sectors: ", read());
- log(" multiple logical blocks per physical: ",
- read() ? "yes" : "no");
- log(" logical blocks per physical: ",
- 1U << read());
- log(" logical block size is above 512 byte: ",
- read() ? "yes" : "no");
- log(" words (16bit) per logical block: ",
- read());
- log(" offset of first logical block within physical: ",
- read());
- }
-};
-
-
-/**
- * 16-bit word big endian device ASCII characters
- */
-template
-struct String
-{
- char buf[DEVICE_STRING::ITEMS + 1];
-
- String(Identity & info)
- {
- long j = 0;
- for (unsigned long i = 0; i < DEVICE_STRING::ITEMS; i++) {
- /* read and swap even and uneven characters */
- char c = (char)info.read(i ^ 1);
- if (Genode::is_whitespace(c) && j == 0)
- continue;
- buf[j++] = c;
- }
-
- buf[j] = 0;
-
- /* remove trailing white spaces */
- while ((j > 0) && (buf[--j] == ' '))
- buf[j] = 0;
- }
-
- bool operator == (char const *other) const
- {
- return strcmp(buf, other) == 0;
- }
-
- void print(Genode::Output &out) const { Genode::print(out, (char const *)buf); }
- char const *cstring() { return buf; }
-};
-
-
-/**
- * Commands to distinguish between ncq and non-ncq operation
- */
-struct Io_command : Interface
-{
- virtual void command(Port &por,
- Command_table &table,
- bool read,
- Block::sector_t block_number,
- size_t count,
- unsigned slot) = 0;
-
- virtual void handle_irq(Port &port, Port::Is::access_t status) = 0;
-};
-
-struct Ncq_command : Io_command
-{
- void command(Port &port,
- Command_table &table,
- bool read,
- Block::sector_t block_number,
- size_t count,
- unsigned slot) override
- {
- table.fis.fpdma(read, block_number, count, slot);
- /* ensure that 'Cmd::St' is 1 before writing 'Sact' */
- port.start();
- /* set pending */
- port.write(1U << slot);
- }
-
- void handle_irq(Port &port, Port::Is::access_t status) override
- {
- /*
- * Check for completions of other requests immediately
- */
- while (Port::Is::Sdbs::get(status = port.read()))
- port.ack_irq();
- }
-};
-
-struct Dma_ext_command : Io_command
-{
- void command(Port &,
- Command_table &table,
- bool read,
- Block::sector_t block_number,
- size_t count,
- unsigned /* slot */) override
- {
- table.fis.dma_ext(read, block_number, count);
- }
-
- void handle_irq(Port &port, Port::Is::access_t status) override
- {
- if (Port::Is::Dma_ext_irq::get(status))
- port.ack_irq();
- }
-};
-
-
-/**
- * Drivers using ncq- and non-ncq commands
- */
-struct Ata_driver : Port_driver
-{
- Genode::Allocator &alloc;
-
- typedef ::String Serial_string;
- typedef ::String Model_string;
-
- Genode::Constructible identity { };
- Genode::Constructible serial { };
- Genode::Constructible model { };
-
- Io_command *io_cmd = nullptr;
- Block::Packet_descriptor pending[32];
-
- Signal_context_capability device_identified;
-
- Ata_driver(Genode::Allocator &alloc,
- Genode::Ram_allocator &ram,
- Ahci_root &root,
- unsigned &sem,
- Genode::Region_map &rm,
- Hba &hba,
- Platform::Hba &platform_hba,
- unsigned number,
- Genode::Signal_context_capability device_identified)
- : Port_driver(ram, root, sem, rm, hba, platform_hba, number),
- alloc(alloc), device_identified(device_identified)
- {
- Port::init();
- identify_device();
- }
-
- ~Ata_driver()
- {
- if (io_cmd)
- destroy(&alloc, io_cmd);
- }
-
- unsigned find_free_cmd_slot()
- {
- for (unsigned slot = 0; slot < cmd_slots; slot++)
- if (!pending[slot].size())
- return slot;
-
- throw Block::Driver::Request_congestion();
- }
-
- void ack_packets()
- {
- unsigned slots = Port::read() | Port::read();
-
- for (unsigned slot = 0; slot < cmd_slots; slot++) {
- if ((slots & (1U << slot)) || !pending[slot].size())
- continue;
-
- Block::Packet_descriptor p = pending[slot];
- pending[slot] = Block::Packet_descriptor();
- ack_packet(p, true);
- }
- }
-
- void overlap_check(Block::sector_t block_number,
- size_t count)
- {
- Block::sector_t end = block_number + count - 1;
-
- for (unsigned slot = 0; slot < cmd_slots; slot++) {
- if (!pending[slot].size())
- continue;
-
- Block::sector_t pending_start = pending[slot].block_number();
- Block::sector_t pending_end = pending_start + pending[slot].block_count() - 1;
- /* check if a pending packet overlaps */
- if ((block_number >= pending_start && block_number <= pending_end) ||
- (end >= pending_start && end <= pending_end) ||
- (pending_start >= block_number && pending_start <= end) ||
- (pending_end >= block_number && pending_end <= end)) {
-
- Genode::warning("overlap: "
- "pending ", pending[slot].block_number(),
- " + ", pending[slot].block_count(), ", "
- "request: ", block_number, " + ", count);
- throw Block::Driver::Request_congestion();
- }
- }
- }
-
- void io(bool read,
- Block::sector_t block_number,
- size_t count,
- addr_t phys,
- Block::Packet_descriptor &packet)
- {
- sanity_check(block_number, count);
- overlap_check(block_number, count);
-
- unsigned slot = find_free_cmd_slot();
- pending[slot] = packet;
-
- /* setup fis */
- Command_table table(command_table_addr(slot), phys, count * block_size());
-
- /* set ATA command */
- io_cmd->command(*this, table, read, block_number, count, slot);
-
- /* set or clear write flag in command header */
- Command_header header(command_header_addr(slot));
- header.write(read ? 0 : 1);
- header.clear_byte_count();
-
- execute(slot);
- }
-
-
- /*****************
- ** Port_driver **
- *****************/
-
- void handle_irq() override
- {
- Is::access_t status = Port::read();
-
- switch (state) {
-
- case IDENTIFY:
-
- if (Port::Is::Dss::get(status)
- || Port::Is::Pss::get(status)
- || Port::Is::Dhrs::get(status)) {
- identity.construct(device_info);
- serial.construct(*identity);
- model.construct(*identity);
-
- if (verbose) {
- Genode::log(" model number: ", Genode::Cstring(model->buf));
- Genode::log(" serial number: ", Genode::Cstring(serial->buf));
- identity->info();
- }
-
- check_device();
- if (ncq_support())
- io_cmd = new (&alloc) Ncq_command();
- else
- io_cmd = new (&alloc) Dma_ext_command();
-
- ack_irq();
-
- Genode::Signal_transmitter(device_identified).submit();
- }
- break;
-
- case READY:
-
- io_cmd->handle_irq(*this, status);
- ack_packets();
-
- default:
- break;
- }
-
- stop();
- }
-
- bool ncq_support()
- {
- return identity->read() && hba.ncq();
- }
-
- void check_device()
- {
- cmd_slots = min((int)cmd_slots,
- identity->read() + 1);
-
- /* no native command queueing */
- if (!ncq_support())
- cmd_slots = 1;
-
- state = READY;
- state_change();
- }
-
- void identify_device()
- {
- state = IDENTIFY;
- addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
-
- Command_table table(command_table_addr(0), phys, 0x1000);
- table.fis.identify_device();
- execute(0);
- }
-
-
- /*****************************
- ** Block::Driver interface **
- *****************************/
-
- bool dma_enabled() override { return true; };
-
- Block::Session::Info info() const override
- {
- return { .block_size = block_size(),
- .block_count = block_count(),
- .align_log2 = log2(block_size()),
- .writeable = true };
- }
-
- void read_dma(Block::sector_t block_number,
- size_t block_count,
- addr_t phys,
- Block::Packet_descriptor &packet) override
- {
- io(true, block_number, block_count, phys, packet);
- }
-
- void write_dma(Block::sector_t block_number,
- size_t block_count,
- addr_t phys,
- Block::Packet_descriptor &packet) override
- {
- io(false, block_number, block_count, phys, packet);
- }
-
- Genode::size_t block_size() const override
- {
- Genode::size_t size = 512;
-
- if (identity->read())
- size = identity->read() / 2;
-
- return size;
- }
-
- Block::sector_t block_count() const override
- {
- return identity->read();
- }
-
- private:
-
- /*
- * Noncopyable
- */
- Ata_driver(Ata_driver const &);
- Ata_driver &operator = (Ata_driver const &);
-};
-
-
-#endif /* _ATA_DRIVER_H_ */
diff --git a/repos/os/src/drivers/ahci/ata_protocol.h b/repos/os/src/drivers/ahci/ata_protocol.h
new file mode 100644
index 0000000000..d3c53ca881
--- /dev/null
+++ b/repos/os/src/drivers/ahci/ata_protocol.h
@@ -0,0 +1,344 @@
+/*
+ * \brief ATA protocol driver
+ * \author Sebastian Sumpf
+ * \date 2015-04-29
+ */
+
+/*
+ * Copyright (C) 2015-2020 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _AHCI__ATA_PROTOCOL_H_
+#define _AHCI__ATA_PROTOCOL_H_
+
+#include
+#include "ahci.h"
+#include "util.h"
+
+namespace Ata {
+ struct Identity;
+ template struct String;
+ class Protocol;
+ using namespace Genode;
+ using namespace Ahci;
+}
+
+/**
+ * Return data of 'identify_device' ATA command
+ */
+struct Ata::Identity : Mmio
+{
+ Identity(addr_t base) : Mmio(base) { }
+
+ struct Serial_number : Register_array<0x14, 8, 20, 8> { };
+ struct Model_number : Register_array<0x36, 8, 40, 8> { };
+
+ struct Queue_depth : Register<0x96, 16>
+ {
+ struct Max_depth : Bitfield<0, 5> { };
+ };
+
+ struct Sata_caps : Register<0x98, 16>
+ {
+ struct Ncq_support : Bitfield<8, 1> { };
+ };
+
+ struct Sector_count : Register<0xc8, 64> { };
+
+ struct Logical_block : Register<0xd4, 16>
+ {
+ struct Per_physical : Bitfield<0, 3> { }; /* 2^X logical per physical */
+ struct Longer_512 : Bitfield<12, 1> { };
+ struct Multiple : Bitfield<13, 1> { }; /* multiple logical blocks per physical */
+ };
+
+ struct Logical_words : Register<0xea, 32> { }; /* words (16 bit) per logical block */
+
+ struct Alignment : Register<0x1a2, 16>
+ {
+ struct Logical_offset : Bitfield<0, 14> { }; /* offset first logical block in physical */
+ };
+
+ void info()
+ {
+ log(" queue depth: ", read() + 1, " "
+ "ncq: ", read());
+ log(" numer of sectors: ", read());
+ log(" multiple logical blocks per physical: ",
+ read() ? "yes" : "no");
+ log(" logical blocks per physical: ",
+ 1U << read());
+ log(" logical block size is above 512 byte: ",
+ read() ? "yes" : "no");
+ log(" words (16bit) per logical block: ",
+ read());
+ log(" offset of first logical block within physical: ",
+ read());
+ }
+};
+
+
+/**
+ * 16-bit word big endian device ASCII characters
+ */
+template
+struct Ata::String
+{
+ char buf[DEVICE_STRING::ITEMS + 1];
+
+ String(Identity & info)
+ {
+ long j = 0;
+ for (unsigned long i = 0; i < DEVICE_STRING::ITEMS; i++) {
+ /* read and swap even and uneven characters */
+ char c = (char)info.read(i ^ 1);
+ if (is_whitespace(c) && j == 0)
+ continue;
+ buf[j++] = c;
+ }
+
+ buf[j] = 0;
+
+ /* remove trailing white spaces */
+ while ((j > 0) && (buf[--j] == ' '))
+ buf[j] = 0;
+ }
+
+ bool operator == (char const *other) const
+ {
+ return strcmp(buf, other) == 0;
+ }
+
+ void print(Output &out) const { print(out, (char const *)buf); }
+ char const *cstring() { return buf; }
+};
+
+
+/**
+ * Protocol driver using ncq- and non-ncq commands
+ */
+class Ata::Protocol : public Ahci::Protocol, Noncopyable
+{
+ private:
+
+ struct Request : Block::Request
+ {
+ bool valid() const { return operation.valid(); }
+ void invalidate() { operation.type = Block::Operation::Type::INVALID; }
+
+ Request & operator=(const Block::Request &request)
+ {
+ operation = request.operation;
+ success = request.success;
+ offset = request.offset;
+ tag = request.tag;
+
+ return *this;
+ }
+ };
+
+ Util::Slots _slots { };
+ unsigned _slot_states = 0;
+
+ typedef String Serial_string;
+ typedef String Model_string;
+
+ Constructible _identity { };
+ bool _writeable { false };
+
+ public:
+
+ Constructible serial { };
+ Constructible model { };
+
+ private:
+
+ bool _overlap_check(Block::Request const &request)
+ {
+ block_number_t block_number = request.operation.block_number;
+ block_number_t end = block_number + request.operation.count - 1;
+
+ auto overlap_check = [&] (Request const &req) {
+ block_number_t pending_start = req.operation.block_number;
+ block_number_t pending_end = pending_start + req.operation.count - 1;
+
+ /* check if a pending packet overlaps */
+ if ((block_number >= pending_start && block_number <= pending_end) ||
+ (end >= pending_start && end <= pending_end) ||
+ (pending_start >= block_number && pending_start <= end) ||
+ (pending_end >= block_number && pending_end <= end)) {
+
+ warning("overlap: "
+ "pending ", pending_start,
+ " + ", req.operation.count, ", "
+ "request: ", block_number, " + ", request.operation.count);
+ return true;
+ }
+
+ return false;
+ };
+
+ return _slots.for_each(overlap_check);
+ }
+
+ bool _ncq_support(Port &port)
+ {
+ return _identity->read() && port.hba.ncq();
+ }
+
+ size_t _block_size() const
+ {
+ size_t size = 512;
+
+ if (_identity->read())
+ size = _identity->read() / 2;
+
+ return size;
+ }
+
+ Block::sector_t _block_count() const
+ {
+ return _identity->read();
+ }
+
+ public:
+
+ /******************************
+ ** Ahci::Protocol interface **
+ ******************************/
+
+ unsigned init(Port &port) override
+ {
+ /* identify device */
+ addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
+
+ Command_table table(port.command_table_addr(0), phys, 0x1000);
+ table.fis.identify_device();
+ port.execute(0);
+
+ port.wait_for_any(port.hba.delayer(), Port::Is::Dss::Equal(1),
+ Port::Is::Pss::Equal(1),
+ Port::Is::Dhrs::Equal(1));
+
+ _identity.construct(port.device_info);
+ serial.construct(*_identity);
+ model.construct(*_identity);
+
+ if (verbose) {
+ log(" model number: ", Cstring(model->buf));
+ log(" serial number: ", Cstring(serial->buf));
+ _identity->info();
+ }
+
+ /* read number of command slots of ATA device */
+ unsigned cmd_slots = _identity->read() + 1;
+
+ /* no native command queueing */
+ if (!_ncq_support(port))
+ cmd_slots = 1;
+
+ _slots.limit((size_t)cmd_slots);
+ port.ack_irq();
+
+ return cmd_slots;
+ }
+
+ void handle_irq(Port &port) override
+ {
+ /* ncg */
+ if (_ncq_support(port))
+ while (Port::Is::Sdbs::get(port.read()))
+ port.ack_irq();
+ /* normal dma */
+ else if (Port::Is::Dma_ext_irq::get(port.read()))
+ port.ack_irq();
+
+ _slot_states = port.read() | port.read();
+ port.stop();
+ }
+
+ Block::Session::Info info() const override
+ {
+ return { .block_size = _block_size(),
+ .block_count = _block_count(),
+ .align_log2 = log2(2ul),
+ .writeable = _writeable };
+ }
+
+ void writeable(bool rw) override { _writeable = rw; }
+
+ Response submit(Port &port, Block::Request const request) override
+ {
+ if (port.sanity_check(request) == false || port.dma_base == 0)
+ return Response::REJECTED;
+
+ if (_writeable == false && request.operation.type == Block::Operation::Type::WRITE)
+ return Response::REJECTED;
+
+ if (_overlap_check(request))
+ return Response::RETRY;
+
+ Request *r = _slots.get();
+
+ if (r == nullptr)
+ return Response::RETRY;
+
+ *r = request;
+
+ Block::Operation op = request.operation;
+ size_t slot = _slots.index(*r);
+ _slot_states |= 1u << slot;
+
+ /* setup fis */
+ Command_table table(port.command_table_addr(slot),
+ port.dma_base + request.offset, /* physical address */
+ op.count * _block_size());
+
+ /* setup ATA command */
+ bool read = op.type == Block::Operation::Type::READ;
+
+ if (_ncq_support(port)) {
+ table.fis.fpdma(read, op.block_number, op.count, slot);
+ /* ensure that 'Cmd::St' is 1 before writing 'Sact' */
+ port.start();
+ /* set pending */
+ port.write(1U << slot);
+ } else {
+ table.fis.dma_ext(read, op.block_number, op.count);
+ }
+
+ /* set or clear write flag in command header */
+ Command_header header(port.command_header_addr(slot));
+ header.write(read ? 0 : 1);
+ header.clear_byte_count();
+
+ port.execute(slot);
+
+ return Response::ACCEPTED;
+ }
+
+ Block::Request completed(Port & /* port */) override
+ {
+ Block::Request r { };
+
+ _slots.for_each([&](Request &request)
+ {
+ size_t index = _slots.index(request);
+ /* request still pending */
+ if (_slot_states & (1u << index))
+ return false;
+
+ r = request;
+ request.invalidate();
+
+ return true;
+ });
+
+ return r;
+ }
+};
+
+#endif /* _AHCI__ATA_PROTOCOL_H_ */
diff --git a/repos/os/src/drivers/ahci/atapi_protocol.h b/repos/os/src/drivers/ahci/atapi_protocol.h
index 3c735cd28c..d552cfeb3c 100644
--- a/repos/os/src/drivers/ahci/atapi_protocol.h
+++ b/repos/os/src/drivers/ahci/atapi_protocol.h
@@ -1,218 +1,195 @@
/*
- * \brief AHCI-port driver for ATAPI devices
+ * \brief ATAPI protocol driver
* \author Sebastian Sumpf
* \date 2015-04-29
*/
/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
+ * Copyright (C) 2015-2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
-#ifndef _ATAPI_DRIVER_H_
-#define _ATAPI_DRIVER_H_
+#ifndef _AHCI__ATAPI_PROTOCOL_H_
+#define _AHCI__ATAPI_PROTOCOL_H_
#include "ahci.h"
#include
+namespace Atapi {
+class Protocol;
+using namespace Ahci;
using namespace Genode;
+}
-struct Atapi_driver : Port_driver
+class Atapi::Protocol : public Ahci::Protocol, Noncopyable
{
- unsigned sense_tries = 0;
- Block::Packet_descriptor pending { };
+ private:
- Atapi_driver(Genode::Ram_allocator &ram,
- Ahci_root &root,
- unsigned &sem,
- Genode::Region_map &rm,
- Hba &hba,
- Platform::Hba &platform_hba,
- unsigned number)
- : Port_driver(ram, root, sem, rm, hba, platform_hba, number)
- {
- Port::init();
- Port::write(1);
- read_sense();
- }
+ Block::Request _pending { };
+ block_number_t _block_count { 0 };
+ size_t _block_size { 2048 };
- 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) {
- Genode::error("could not power up device");
- state_change();
- return;
+ void _atapi_command(Port &port)
+ {
+ Command_header header(port.command_header_addr(0));
+ header.atapi_command();
+ header.clear_byte_count();
+ port.execute(0);
}
- addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
+ void _read_sense(Port &port)
+ {
+ addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
- Command_table table(command_table_addr(0), phys, 0x1000);
- table.fis.atapi();
- table.atapi_cmd.read_sense();
+ Command_table table(port.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();
-
- if (slots & 1 || !pending.size())
- return;
-
- Block::Packet_descriptor p = pending;
- pending = Block::Packet_descriptor();
- ack_packet(p, true);
- }
-
-
- /*****************
- ** Port_driver **
- *****************/
-
- void handle_irq() override
- {
- Is::access_t status = Port::read();
-
- if (verbose) {
- Genode::log("irq: "
- "is: ", Genode::Hex(status), " "
- "ci: ", Genode::Hex(Port::read()), " "
- "state: ", (int)state);
- Device_fis f(fis_base);
- Genode::log("d2h: "
- "status: ", f.read(), " "
- "error: ", Genode::Hex(f.read()));
+ _atapi_command(port);
}
- ack_irq();
+ void _test_unit_ready(Port &port)
+ {
+ Command_table table(port.command_table_addr(0), 0, 0);
+ table.fis.atapi();
+ table.atapi_cmd.test_unit_ready();
- if (state == TEST_READY && Port::Is::Dhrs::get(status)) {
- Device_fis f(fis_base);
-
- /* check if devic is ready */
- if (f.read() && !f.read())
- read_capacity();
- else
- read_sense();
+ _atapi_command(port);
}
- if (state == READY && Port::Is::Dhrs::get(status)) {
- ack_packets();
+ void _read_capacity(Port &port)
+ {
+ addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
+
+ Command_table table(port.command_table_addr(0), phys, 0x1000);
+ table.fis.atapi();
+ table.fis.byte_count(~0);
+
+ table.atapi_cmd.read_capacity();
+
+ _atapi_command(port);
}
- if (Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) {
- switch (state) {
+ void _start_unit(Port &port)
+ {
+ Command_table table(port.command_table_addr(0), 0, 0);
+ table.fis.atapi();
+ table.atapi_cmd.start_unit();
- case STATUS:
- test_unit_ready();
- break;
-
- case IDENTIFY:
- state = READY;
- state_change();
- break;
-
- case READY:
- ack_packets();
- return;
-
- default:
- break;
- }
+ _atapi_command(port);
}
- }
- Block::Session::Info info() const override
- {
- return { .block_size = block_size(),
- .block_count = block_count(),
- .align_log2 = 11,
- .writeable = false };
- }
+ public:
+ /******************************
+ ** Ahci::Protocol interface **
+ ******************************/
- /*****************************
- ** Block::Driver interface **
- *****************************/
+ unsigned init(Port &port) override
+ {
+ port.write(1);
- bool dma_enabled() override { return true; };
+ retry(
+ [&] {
- Genode::size_t block_size() const override
- {
- return host_to_big_endian(((unsigned *)device_info)[1]);
- }
+ _start_unit(port);
+ port.wait_for_any(port.hba.delayer(),
+ Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
+ Port::Is::Dhrs::Equal(1));
+ port.ack_irq();
- Block::sector_t block_count() const override
- {
- return host_to_big_endian(((unsigned *)device_info)[0]) + 1;
- }
+ /* read sense */
+ _read_sense(port);
+ port.wait_for_any(port.hba.delayer(),
+ Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
+ Port::Is::Dhrs::Equal(1));
+ port.ack_irq();
- void read_dma(Block::sector_t block_number,
- size_t count,
- addr_t phys,
- Block::Packet_descriptor &packet) override
- {
- if (pending.size())
- throw Block::Driver::Request_congestion();
+ /* test unit ready */
+ _test_unit_ready(port);
+ port.wait_for(port.hba.delayer(), Port::Is::Dhrs::Equal(1));
+ port.ack_irq();
- sanity_check(block_number, count);
+ Device_fis f(port.fis_base);
+ /* check if devic is ready */
+ if (!f.read() || f.read())
+ throw Port::Polling_timeout();
- pending = packet;
+ _read_capacity(port);
+ port.wait_for_any(port.hba.delayer(),
+ Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
+ Port::Is::Dhrs::Equal(1));
+ port.ack_irq();
- if (verbose)
- Genode::log("add packet read ", block_number, " count ", count, " -> 0");
+ _block_count = host_to_big_endian(((unsigned *)port.device_info)[0]) + 1;
+ _block_size = host_to_big_endian(((unsigned *)port.device_info)[1]);
+ },
+ [&] {}, 3);
- /* setup fis */
- Command_table table(command_table_addr(0), phys, count * block_size());
- table.fis.atapi();
+ return 1;
+ }
- /* setup atapi command */
- table.atapi_cmd.read10(block_number, count);
+ Block::Session::Info info() const override
+ {
+ return { .block_size = _block_size,
+ .block_count = _block_count,
+ .align_log2 = 1,
+ .writeable = false };
+ }
- /* set and clear write flag in command header */
- Command_header header(command_header_addr(0));
- header.write(0);
- header.clear_byte_count();
+ void handle_irq(Port &port) override
+ {
+ port.ack_irq();
+ }
- /* set pending */
- execute(0);
- }
+ void writeable(bool) override { }
+
+ Response submit(Port &port, Block::Request const request) override
+ {
+ if (port.sanity_check(request) == false || port.dma_base == 0 ||
+ request.operation.type != Block::Operation::Type::READ)
+ return Response::REJECTED;
+
+ if (_pending.operation.valid())
+ return Response::RETRY;
+
+ Block::Operation op = request.operation;
+ _pending = request;
+ _pending.success = false;
+
+ /* setup fis */
+ Command_table table(port.command_table_addr(0),
+ port.dma_base + request.offset,
+ op.count * _block_size);
+ table.fis.atapi();
+
+ /* setup atapi command */
+ table.atapi_cmd.read10(op.block_number, op.count);
+ table.fis.byte_count(~0);
+
+ /* set and clear write flag in command header */
+ Command_header header(port.command_header_addr(0));
+ header.write(0);
+ header.clear_byte_count();
+
+ /* set pending */
+ port.execute(0);
+
+ return Response::ACCEPTED;
+ }
+
+ Block::Request completed(Port &port) override
+ {
+ if (!_pending.operation.valid() || port.read())
+ return Block::Request();
+
+ Block::Request request = _pending;
+ _pending.operation.type = Block::Operation::Type::INVALID;
+
+ return request;
+ }
};
-#endif /* _ATAPI_DRIVER_H_ */
+#endif /* _AHCI__ATAPI_PROTOCOL_H_ */
diff --git a/repos/os/src/drivers/ahci/main.cc b/repos/os/src/drivers/ahci/main.cc
index ccfeaa19c5..383667f908 100644
--- a/repos/os/src/drivers/ahci/main.cc
+++ b/repos/os/src/drivers/ahci/main.cc
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2016-2017 Genode Labs GmbH
+ * Copyright (C) 2016-2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@@ -14,195 +14,394 @@
/* Genode includes */
#include
#include
-#include
#include
-#include
+#include
#include
+#include
#include
#include
/* local includes */
#include
+#include
+#include
-
-namespace Block {
- class Factory;
- class Root_multiple_clients;
- class Main;
+namespace Ahci {
+ struct Dispatch;
+ class Driver;
+ struct Main;
+ struct Block_session_handler;
+ struct Block_session_component;
}
-struct Block::Factory : Driver_factory
+struct Ahci::Dispatch : Interface
{
- long device_num;
-
- Block::Driver *create() override
- {
- return Ahci_driver::claim_port(device_num);
- }
-
- void destroy(Block::Driver *) override
- {
- Ahci_driver::free_port(device_num);
- }
-
- Factory(long device_num) : device_num(device_num) { }
+ virtual void session(unsigned index) = 0;
};
-class Session_component : public Block::Session_component
+class Ahci::Driver : Noncopyable
{
public:
- Session_component(Block::Driver_factory &driver_factory,
- Genode::Entrypoint &ep,
- Genode::Region_map &rm,
- Genode::size_t buf_size,
- bool writeable)
- : Block::Session_component(driver_factory, ep, rm, buf_size, writeable) { }
+ enum { MAX_PORTS = 32 };
- Block::Driver_factory &factory() { return _driver_factory; }
-};
-
-
-class Block::Root_multiple_clients : public Root_component< ::Session_component>,
- public Ahci_root
-{
private:
- Genode::Env &_env;
- Genode::Allocator &_alloc;
- Genode::Xml_node _config;
+ Env &_env;
+ Dispatch &_dispatch;
- protected:
- ::Session_component *_create_session(const char *args) override
+ struct Timer_delayer : Mmio::Delayer, Timer::Connection
{
- Session_label const label = label_from_args(args);
- Session_policy const policy(label, _config);
+ Timer_delayer(Env &env)
+ : Timer::Connection(env) { }
- size_t ram_quota =
- Arg_string::find_arg(args, "ram_quota").ulong_value(0);
- size_t tx_buf_size =
- Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
+ void usleep(uint64_t us) override { Timer::Connection::usleep(us); }
+ } _delayer { _env };
- if (!tx_buf_size)
- throw Service_denied();
+ Hba _hba { _env, _delayer };
- size_t session_size = sizeof(::Session_component)
- + sizeof(Factory) + tx_buf_size;
+ Constructible _ata[MAX_PORTS];
+ Constructible _atapi[MAX_PORTS];
+ Constructible _ports[MAX_PORTS];
- if (max((size_t)4096, session_size) > ram_quota) {
- error("insufficient 'ram_quota' from '", label, "',"
- " got ", ram_quota, ", need ", session_size);
- throw Insufficient_ram_quota();
- }
+ Signal_handler _irq { _env.ep(), *this, &Driver::handle_irq };
+ bool _enable_atapi;
- /* try read device port number attribute */
- long num = policy.attribute_value("device", -1L);
-
- /* try read device model and serial number attributes */
- auto const model = policy.attribute_value("model", String<64>());
- auto const serial = policy.attribute_value("serial", String<64>());
-
- /* sessions are not writeable by default */
- bool writeable = policy.attribute_value("writeable", false);
-
- /* prefer model/serial routing */
- if ((model != "") && (serial != ""))
- num = Ahci_driver::device_number(model.string(), serial.string());
-
- if (num < 0) {
- error("rejecting session request, no matching policy for '", label, "'",
- model == "" ? ""
- : " (model=", model, " serial=", serial, ")");
- throw Service_denied();
- }
-
- if (!Ahci_driver::avail(num)) {
- error("Device ", num, " not available");
- throw Service_denied();
- }
-
- if (writeable)
- writeable = Arg_string::find_arg(args, "writeable").bool_value(true);
-
- Block::Factory *factory = new (&_alloc) Block::Factory(num);
- ::Session_component *session = new (&_alloc)
- ::Session_component(*factory, _env.ep(), _env.rm(), tx_buf_size, writeable);
- log(
- writeable ? "writeable " : "read-only ",
- "session opened at device ", num, " for '", label, "'");
- return session;
+ void _info()
+ {
+ log("version: "
+ "major=", Hex(_hba.read()), " "
+ "minor=", Hex(_hba.read()));
+ log("command slots: ", _hba.command_slots());
+ log("native command queuing: ", _hba.ncq() ? "yes" : "no");
+ log("64-bit support: ", _hba.supports_64bit() ? "yes" : "no");
}
- void _destroy_session(::Session_component *session) override
+ void _scan_ports(Region_map &rm)
{
- Driver_factory &factory = session->factory();
- Genode::destroy(&_alloc, session);
- Genode::destroy(&_alloc, &factory);
+ log("number of ports: ", _hba.port_count(), " pi: ",
+ Hex(_hba.read()));
+
+ for (unsigned index = 0; index < MAX_PORTS; index++) {
+
+ Port_base port(index, _hba);
+
+ if (port.implemented() == false)
+ continue;
+
+ bool enabled = false;
+ if (port.ata()) {
+ try {
+ _ata[index].construct();
+ _ports[index].construct(*_ata[index], rm, _hba, index);
+ enabled = true;
+ } catch (...) { }
+
+ log("\t\t#", index, ":", enabled ? " ATA" : " off (ATA)");
+ } else if (port.atapi() && _enable_atapi) {
+ try {
+ _atapi[index].construct();
+ _ports[index].construct(*_atapi[index], rm, _hba, index);
+ enabled = true;
+ } catch (...) { }
+
+ log("\t\t#", index, ":", enabled ? " ATAPI" : " off (ATAPI)");
+ } else {
+ log("\t\t#", index, ":", port.atapi() ? " off (ATAPI)"
+ : " off (unknown device signature)");
+ }
+ }
}
public:
- Root_multiple_clients(Genode::Env &env, Genode::Allocator &alloc,
- Genode::Xml_node config)
- :
- Root_component(&env.ep().rpc_ep(), &alloc),
- _env(env), _alloc(alloc), _config(config)
- { }
-
- Genode::Entrypoint &entrypoint() override { return _env.ep(); }
-
- void announce() override
+ Driver(Env &env, Dispatch &dispatch, bool support_atapi)
+ : _env(env), _dispatch(dispatch), _enable_atapi(support_atapi)
{
- _env.parent().announce(_env.ep().manage(*this));
+ _info();
+
+ /* register irq handler */
+ _hba.sigh_irq(_irq);
+
+ /* initialize HBA (IRQs, memory) */
+ _hba.init();
+
+ /* search for devices */
+ _scan_ports(env.rm());
+ }
+
+ /**
+ * Forward IRQs to ports/block sessions
+ */
+ void handle_irq()
+ {
+ unsigned port_list = _hba.read();
+ while (port_list) {
+ unsigned port = log2(port_list);
+ port_list &= ~(1U << port);
+
+ /* ack irq */
+ if (_ports[port].constructed())
+ _ports[port]->handle_irq();
+
+ /* handle (pending) requests */
+ _dispatch.session(port);
+ }
+
+ /* clear status register */
+ _hba.ack_irq();
+ }
+ Port &port(long device, char const *model_num, char const *serial_num)
+ {
+ /* check for model/device */
+ if (model_num && serial_num) {
+ for (long index = 0; index < MAX_PORTS; index++) {
+ if (!_ata[index].constructed()) continue;
+
+ Ata::Protocol &protocol = *_ata[index];
+ if (*protocol.model == model_num && *protocol.serial == serial_num)
+ return *_ports[index];
+ }
+ }
+
+ /* check for device number */
+ if (device >= 0 && device < MAX_PORTS && _ports[device].constructed())
+ return *_ports[device];
+
+ throw -1;
+ }
+
+ template void for_each_port(FN const &fn)
+ {
+ for (unsigned index = 0; index < MAX_PORTS; index++) {
+ if (!_ports[index].constructed()) continue;
+ fn(*_ports[index], index, !_ata[index].constructed());
+ }
+ }
+
+ void report_ports(Reporter &reporter)
+ {
+ auto report = [&](Port const &port, unsigned index, bool atapi) {
+
+ Block::Session::Info info = port.info();
+ Reporter::Xml_generator xml(reporter, [&] () {
+
+ xml.node("port", [&] () {
+ xml.attribute("num", index);
+ xml.attribute("type", atapi ? "ATAPI" : "ATA");
+ xml.attribute("block_count", info.block_count);
+ xml.attribute("block_size", info.block_size);
+ if (!atapi) {
+ xml.attribute("model", _ata[index]->model->cstring());
+ xml.attribute("serial", _ata[index]->serial->cstring());
+ }
+ });
+ });
+ };
+
+ for_each_port(report);
}
};
-struct Block::Main
+struct Ahci::Block_session_handler : Interface
{
- Genode::Env &env;
- Genode::Heap heap { env.ram(), env.rm() };
+ Env &env;
+ Port &port;
+ Ram_dataspace_capability ds;
- Genode::Attached_rom_dataspace config { env, "config" };
+ Signal_handler request_handler
+ { env.ep(), *this, &Block_session_handler::handle};
- Genode::Constructible reporter { };
+ Block_session_handler(Env &env, Port &port, size_t buffer_size)
+ : env(env), port(port), ds(port.alloc_buffer(buffer_size))
+ { }
- Block::Root_multiple_clients root;
-
- Signal_handler device_identified {
- env.ep(), *this, &Main::handle_device_identified };
-
- Main(Genode::Env &env)
- : env(env), root(env, heap, config.xml())
+ ~Block_session_handler()
{
- Genode::log("--- Starting AHCI driver ---");
+ port.free_buffer(ds);
+ }
+
+ virtual void handle_requests()= 0;
+
+ void handle()
+ {
+ handle_requests();
+ }
+};
+
+struct Ahci::Block_session_component : Rpc_object,
+ Block_session_handler,
+ Block::Request_stream
+{
+ Block_session_component(Env &env, Port &port, size_t buffer_size)
+ :
+ Block_session_handler(env, port, buffer_size),
+ Request_stream(env.rm(), ds, env.ep(), request_handler, port.info())
+ {
+ env.ep().manage(*this);
+ }
+
+ ~Block_session_component()
+ {
+ env.ep().dissolve(*this);
+ }
+
+ Info info() const override { return Request_stream::info(); }
+
+ Capability tx_cap() override { return Request_stream::tx_cap(); }
+
+ void handle_requests() override
+ {
+ while (true) {
+
+ bool progress = false;
+
+ /*
+ * Acknowledge any pending packets before sending new request to the
+ * controller.
+ */
+ try_acknowledge([&](Ack &ack) {
+ port.for_one_completed_request([&] (Block::Request request) {
+ progress = true;
+ ack.submit(request);
+ });
+ });
+
+ with_requests([&] (Block::Request request) {
+
+ Response response = Response::RETRY;
+
+ /* only READ/WRITE requests, others are noops for now */
+ if (Block::Operation::has_payload(request.operation.type) == false) {
+ request.success = true;
+ progress = true;
+ return Response::REJECTED;
+ }
+
+ if ((response = port.submit(request)) != Response::RETRY)
+ progress = true;
+
+ return response;
+ });
+
+ if (progress == false) break;
+ }
+
+ /* poke */
+ wakeup_client_if_needed();
+ }
+};
+
+
+struct Ahci::Main : Rpc_object>,
+ Dispatch
+{
+ Env &env;
+
+ Attached_rom_dataspace config { env, "config" };
+
+ Constructible driver { };
+ Constructible reporter { };
+ Constructible block_session[Driver::MAX_PORTS];
+
+ Main(Env &env)
+ : env(env)
+ {
+ log("--- Starting AHCI driver ---");
bool support_atapi = config.xml().attribute_value("atapi", false);
try {
- Ahci_driver::init(env, heap, root, support_atapi, device_identified);
- } catch (Ahci_driver::Missing_controller) {
- Genode::error("no AHCI controller found");
+ driver.construct(env, *this, support_atapi);
+ report_ports();
+ } catch (Ahci::Missing_controller) {
+ error("no AHCI controller found");
env.parent().exit(~0);
- } catch (Genode::Service_denied) {
- Genode::error("hardware access denied");
+ } catch (Service_denied) {
+ error("hardware access denied");
env.parent().exit(~0);
}
+
+ env.parent().announce(env.ep().manage(*this));
+ }
+
+ void session(unsigned index) override
+ {
+ if (index > Driver::MAX_PORTS || !block_session[index].constructed()) return;
+ block_session[index]->handle_requests();
+ }
+
+ Session_capability session(Root::Session_args const &args,
+ Affinity const &) override
+ {
+ Session_label const label = label_from_args(args.string());
+ Session_policy const policy(label, config.xml());
+
+ Ram_quota const ram_quota = ram_quota_from_args(args.string());
+ size_t const tx_buf_size =
+ Arg_string::find_arg(args.string(), "tx_buf_size").ulong_value(0);
+
+ if (!tx_buf_size)
+ throw Service_denied();
+
+ if (tx_buf_size > ram_quota.value) {
+ error("insufficient 'ram_quota' from '", label, "',"
+ " got ", ram_quota, ", need ", tx_buf_size);
+ throw Insufficient_ram_quota();
+ }
+
+ /* try read device port number attribute */
+ long device = policy.attribute_value("device", -1L);
+
+ /* try read device model and serial number attributes */
+ auto const model = policy.attribute_value("model", String<64>());
+ auto const serial = policy.attribute_value("serial", String<64>());
+ bool const writeable = policy.attribute_value("writeable", false);
+
+ try {
+ Port &port = driver->port(device, model.string(), serial.string());
+
+ if (block_session[port.index].constructed()) {
+ error("Device with number=", port.index, " is already in use");
+ throw Service_denied();
+ }
+
+ port.writeable(writeable);
+ block_session[port.index].construct(env, port, tx_buf_size);
+ return block_session[port.index]->cap();
+ } catch (...) {
+ error("rejecting session request, no matching policy for '", label, "'",
+ " (model=", model, " serial=", serial, " device index=", device, ")");
+ }
+
+ throw Service_denied();
+ }
+
+ void upgrade(Session_capability, Root::Upgrade_args const&) override { }
+
+ void close(Session_capability cap) override
+ {
+ for (int index = 0; index < Driver::MAX_PORTS; index++) {
+ if (!block_session[index].constructed() || !(cap == block_session[index]->cap()))
+ continue;
+
+ block_session[index].destruct();
+ }
}
- void handle_device_identified()
+ void report_ports()
{
try {
Xml_node report = config.xml().sub_node("report");
if (report.attribute_value("ports", false)) {
reporter.construct(env, "ports");
reporter->enabled(true);
- Ahci_driver::report_ports(*reporter);
+ driver->report_ports(*reporter);
}
- } catch (Genode::Xml_node::Nonexistent_sub_node) { }
+ } catch (Xml_node::Nonexistent_sub_node) { }
}
};
-
-void Component::construct(Genode::Env &env) { static Block::Main server(env); }
+void Component::construct(Genode::Env &env) { static Ahci::Main server(env); }
diff --git a/repos/os/src/drivers/ahci/spec/x86/platform.cc b/repos/os/src/drivers/ahci/spec/x86/platform.cc
index 47aa46d96b..439bd3cd99 100644
--- a/repos/os/src/drivers/ahci/spec/x86/platform.cc
+++ b/repos/os/src/drivers/ahci/spec/x86/platform.cc
@@ -1,156 +1,87 @@
/*
* \brief Driver for PCI-bus platforms
* \author Sebastian Sumpf
- * \date 2015-04-29
+ * \date 2020-01-20
*/
/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
+ * Copyright (C) 2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
-#include
-#include
-#include
-#include
-
#include
-
-using namespace Genode;
-
-struct X86_hba : Platform::Hba
+Ahci::Data::Data(Env &env)
+ : env(env)
{
- 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 */
- PCI_CMD = 0x4,
- };
+ pci_device_cap = pci.with_upgrade(
+ [&] () { return pci.next_device(pci_device_cap, AHCI_DEVICE,
+ CLASS_MASK); });
- Genode::Env &env;
-
- Platform::Connection pci { env };
- Platform::Device_capability pci_device_cap { };
- Constructible pci_device { };
- Constructible irq { };
- addr_t res_base { 0 };
- size_t res_size { 0 };
-
- X86_hba(Genode::Env &env) : env(env)
- {
- pci_device_cap = pci.with_upgrade(
- [&] () { return pci.next_device(pci_device_cap, AHCI_DEVICE,
- CLASS_MASK); });
-
- if (!pci_device_cap.valid()) {
- throw Ahci_driver::Missing_controller();
- }
-
- /* construct pci client */
- pci_device.construct(pci_device_cap);
- Genode::log("AHCI found ("
- "vendor: ", Genode::Hex(pci_device->vendor_id()), " "
- "device: ", Genode::Hex(pci_device->device_id()), " "
- "class: ", Genode::Hex(pci_device->class_code()), ")");
-
- /* read base address of controller */
- Platform::Device::Resource resource = pci_device->resource(AHCI_BASE_ID);
- res_base = resource.base();
- res_size = resource.size();
-
- uint16_t cmd = pci_device->config_read(PCI_CMD, Platform::Device::ACCESS_16BIT);
- cmd |= 0x2; /* respond to memory space accesses */
- cmd |= 0x4; /* enable bus master */
- _config_write(PCI_CMD, cmd, Platform::Device::ACCESS_16BIT);
-
- irq.construct(pci_device->irq(0));
+ if (!pci_device_cap.valid()) {
+ throw Missing_controller();
}
- void disable_msi()
- {
- enum { PM_CAP_OFF = 0x34, MSI_CAP = 0x5, MSI_ENABLED = 0x1 };
- uint8_t cap = pci_device->config_read(PM_CAP_OFF, Platform::Device::ACCESS_8BIT);
+ /* construct pci client */
+ pci_device.construct(pci_device_cap);
+ log("AHCI found ("
+ "vendor: ", Hex(pci_device->vendor_id()), " "
+ "device: ", Hex(pci_device->device_id()), " "
+ "class: ", Hex(pci_device->class_code()), ")");
- /* iterate through cap pointers */
- for (uint16_t val = 0; cap; cap = val >> 8) {
- val = pci_device->config_read(cap, Platform::Device::ACCESS_16BIT);
+ /* map base address of controller */
+ Io_mem_session_capability iomem_cap = pci_device->io_mem(pci_device->phys_bar_to_virt(AHCI_BASE_ID));
+ iomem.construct(env.rm(), Io_mem_session_client(iomem_cap).dataspace());
- if ((val & 0xff) != MSI_CAP)
- continue;
+ uint16_t cmd = pci_device->config_read(PCI_CMD, ::Platform::Device::ACCESS_16BIT);
+ cmd |= 0x2; /* respond to memory space accesses */
+ cmd |= 0x4; /* enable bus master */
+ _config_write(PCI_CMD, cmd, ::Platform::Device::ACCESS_16BIT);
- uint16_t msi = pci_device->config_read(cap + 2, Platform::Device::ACCESS_16BIT);
-
- if (msi & MSI_ENABLED) {
- _config_write(cap + 2, msi ^ MSI_CAP,
- Platform::Device::ACCESS_8BIT);
- Genode::log("disabled MSI ", msi);
- }
- }
- }
-
- void _config_write(uint8_t op, uint16_t cmd,
- Platform::Device::Access_size width)
- {
- Genode::size_t donate = 4096;
- Genode::retry(
- [&] () {
- Genode::retry(
- [&] () { pci_device->config_write(op, cmd, width); },
- [&] () { pci.upgrade_caps(2); });
- },
- [&] () {
- pci.upgrade_ram(donate);
- donate *= 2;
- });
- }
-
-
- /*******************
- ** Hba interface **
- *******************/
-
- Genode::addr_t base() const override { return res_base; }
- Genode::size_t size() const override { return res_size; }
-
- void sigh_irq(Signal_context_capability sigh) override
- {
- irq->sigh(sigh);
- ack_irq();
- }
-
- void ack_irq() override { irq->ack_irq(); }
-
- Ram_dataspace_capability
- alloc_dma_buffer(Genode::size_t size) override
- {
- size_t donate = size;
-
- return retry(
- [&] () {
- return retry(
- [&] () { return pci.alloc_dma_buffer(size); },
- [&] () { pci.upgrade_caps(2); });
- },
- [&] () {
- pci.upgrade_ram(donate);
- donate = donate * 2 > size ? 4096 : donate * 2;
- });
- }
-
- void free_dma_buffer(Genode::Ram_dataspace_capability ds) override
- {
- pci.free_dma_buffer(ds);
- }
-};
-
-
-Platform::Hba &Platform::init(Genode::Env &env, Mmio::Delayer &)
-{
- static X86_hba h(env);
- return h;
+ irq.construct(pci_device->irq(0));
+}
+
+
+/************************
+ ** Platform interface **
+ ************************/
+
+Genode::addr_t Ahci::Platform::_mmio_base() const
+{
+ return addr_t(_data.iomem->local_addr());
+}
+
+
+void Ahci::Platform::sigh_irq(Signal_context_capability sigh)
+{
+ _data.irq->sigh(sigh);
+ ack_irq();
+}
+
+
+void Ahci::Platform::ack_irq() { _data.irq->ack_irq(); }
+
+
+Genode::Ram_dataspace_capability Ahci::Platform::alloc_dma_buffer(size_t size)
+{
+ size_t donate = size;
+
+ return retry(
+ [&] () {
+ return retry(
+ [&] () { return _data.pci.alloc_dma_buffer(size); },
+ [&] () { _data.pci.upgrade_caps(2); });
+ },
+ [&] () {
+ _data.pci.upgrade_ram(donate);
+ donate = donate * 2 > size ? 4096 : donate * 2;
+ });
+}
+
+
+void Ahci::Platform::free_dma_buffer(Genode::Ram_dataspace_capability ds)
+{
+ _data.pci.free_dma_buffer(ds);
}
diff --git a/repos/os/src/drivers/ahci/spec/x86/platform.h b/repos/os/src/drivers/ahci/spec/x86/platform.h
new file mode 100644
index 0000000000..78a5778449
--- /dev/null
+++ b/repos/os/src/drivers/ahci/spec/x86/platform.h
@@ -0,0 +1,65 @@
+#ifndef _AHCI__SPEC__X86__PLATFORM_H_
+#define _AHCI__SPEC__X86__PLATFORM_H_
+/*
+ * \brief Driver for PCI-bus platforms
+ * \author Sebastian Sumpf
+ * \date 2020-01-20
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#include
+#include
+#include
+#include
+
+namespace Ahci {
+ struct Data;
+ using namespace Genode;
+}
+
+struct Ahci::Data
+{
+ enum Pci_config {
+ CLASS_MASS_STORAGE = 0x10000u,
+ SUBCLASS_AHCI = 0x600u,
+ CLASS_MASK = 0xffff00u,
+ AHCI_DEVICE = CLASS_MASS_STORAGE | SUBCLASS_AHCI,
+ AHCI_BASE_ID = 0x5, /* resource id of ahci base addr */
+ PCI_CMD = 0x4,
+ };
+
+ Genode::Env &env;
+
+ Platform::Connection pci { env };
+ Platform::Device_capability pci_device_cap { };
+ Constructible pci_device { };
+ Constructible irq { };
+ Constructible iomem { };
+
+ Data(Env &env);
+
+ void _config_write(uint8_t op, uint16_t cmd,
+ Platform::Device::Access_size width)
+ {
+ size_t donate = 4096;
+ retry(
+ [&] () {
+ retry(
+ [&] () { pci_device->config_write(op, cmd, width); },
+ [&] () { pci.upgrade_caps(2); });
+ },
+ [&] () {
+ pci.upgrade_ram(donate);
+ donate *= 2;
+ });
+ }
+};
+
+
+#endif /* _AHCI__SPEC__X86__PLATFORM_H_ */
diff --git a/repos/os/src/drivers/ahci/spec/x86/target.mk b/repos/os/src/drivers/ahci/spec/x86/target.mk
index 6cf2512e5f..fe44856e5b 100644
--- a/repos/os/src/drivers/ahci/spec/x86/target.mk
+++ b/repos/os/src/drivers/ahci/spec/x86/target.mk
@@ -1,5 +1,7 @@
TARGET = ahci_drv
REQUIRES = x86
+INC_DIR += $(REP_DIR)/src/drivers/ahci/spec/x86
+
include $(REP_DIR)/src/drivers/ahci/target.inc
diff --git a/repos/os/src/drivers/ahci/target.inc b/repos/os/src/drivers/ahci/target.inc
index 295ae130b6..a510cd8753 100644
--- a/repos/os/src/drivers/ahci/target.inc
+++ b/repos/os/src/drivers/ahci/target.inc
@@ -1,4 +1,4 @@
-SRC_CC += main.cc ahci.cc platform.cc
+SRC_CC += main.cc platform.cc
INC_DIR += $(REP_DIR)/src/drivers/ahci
LIBS += base
diff --git a/repos/os/src/drivers/ahci/util.h b/repos/os/src/drivers/ahci/util.h
new file mode 100644
index 0000000000..0f8643e84b
--- /dev/null
+++ b/repos/os/src/drivers/ahci/util.h
@@ -0,0 +1,71 @@
+/*
+ * \brief Utilitize used by the AHCI driver
+ * \author Josef Soentgen
+ * \date 2018-03-05
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _AHCI_UTIL_H_
+#define _AHCI_UTIL_H_
+
+/* Genode includes */
+#include
+
+namespace Util {
+
+ using namespace Genode;
+ /*
+ * Wrap array into convinient interface
+ *
+ * The used datatype T must implement the following methods:
+ *
+ * bool valid() const returns true if the object is valid
+ * void invalidate() adjusts the object so that valid() returns false
+ */
+ template
+ struct Slots
+ {
+ T _entries[CAP] { };
+ size_t _limit { CAP };
+
+ /**
+ * Get free slot
+ */
+ T *get()
+ {
+ for (size_t i = 0; i < _limit; i++) {
+ if (!_entries[i].valid()) { return &_entries[i]; }
+ }
+ return nullptr;
+ }
+
+ /**
+ * Iterate over all slots until FUNC returns true
+ */
+ template
+ bool for_each(FUNC const &func)
+ {
+ for (size_t i = 0; i < _limit; i++) {
+ if (!_entries[i].valid()) { continue; }
+ if ( func(_entries[i])) { return true; }
+ }
+ return false;
+ }
+
+ size_t index(T const &entry) const
+ {
+ size_t index = &entry - _entries;
+ return index;
+ }
+
+ void limit(size_t limit) { _limit = limit; }
+ };
+}
+
+#endif /* _AHCI_UTIL_H_ */