From 8a7deae238c1d883b3e0e583a3fff1c3ab14be40 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Wed, 8 Jan 2020 12:58:32 +0100 Subject: [PATCH] ahci: switch to request stream API + structural changes - Remove dated 'Block::Driver' front end and implement 'Block::Request_stream' front end - Remove all dynamic memory allocations but DMA memory allocations - Remove 'Platform_hba' and implement platform specific functions in 'spec//*' - Ata and Atapi don't inherit from 'Port' any more, but are a member of 'Port' as a protocol implementation - Use platform driver for MMIO mappings (x86) - Exchange stateful initialization of Ata/Atapi with a sequential initialization using 'wait_for_any' and 'retry' patterns - Fix Atapi initialization by setting the byte count limit - Set FIS receive base only when Cmd::FRE is disabled and Cmd::FR is 0 - Put everything in namespaces ('Ahci', 'Ata', or 'Atapi') - Ata decides during read/write operations to use native-command queuing or normal DMA requests - Remove port claiming logic (is now done via 'Constructibles') fixes #3636 --- repos/os/run/ahci_block.run | 16 +- repos/os/src/drivers/ahci/ahci.cc | 278 ------ repos/os/src/drivers/ahci/ahci.h | 801 ++++++++++-------- repos/os/src/drivers/ahci/ata_driver.h | 427 ---------- repos/os/src/drivers/ahci/ata_protocol.h | 344 ++++++++ repos/os/src/drivers/ahci/atapi_protocol.h | 315 ++++--- repos/os/src/drivers/ahci/main.cc | 459 +++++++--- .../os/src/drivers/ahci/spec/x86/platform.cc | 201 ++--- repos/os/src/drivers/ahci/spec/x86/platform.h | 65 ++ repos/os/src/drivers/ahci/spec/x86/target.mk | 2 + repos/os/src/drivers/ahci/target.inc | 2 +- repos/os/src/drivers/ahci/util.h | 71 ++ 12 files changed, 1471 insertions(+), 1510 deletions(-) delete mode 100644 repos/os/src/drivers/ahci/ahci.cc delete mode 100644 repos/os/src/drivers/ahci/ata_driver.h create mode 100644 repos/os/src/drivers/ahci/ata_protocol.h create mode 100644 repos/os/src/drivers/ahci/spec/x86/platform.h create mode 100644 repos/os/src/drivers/ahci/util.h diff --git a/repos/os/run/ahci_block.run b/repos/os/run/ahci_block.run index a92393e38..3fbb15f9c 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 4be53f333..000000000 --- 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 d72c2e4e3..31b87541a 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 538ee9e4b..000000000 --- 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 000000000..d3c53ca88 --- /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 3c735cd28..d552cfeb3 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 ccfeaa19c..383667f90 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 47aa46d96..439bd3cd9 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 000000000..78a577844 --- /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 6cf2512e5..fe44856e5 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 295ae130b..a510cd875 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 000000000..0f8643e84 --- /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_ */