diff --git a/os/lib/mk/ahci.inc b/os/lib/mk/ahci.inc new file mode 100644 index 000000000..a3ceab080 --- /dev/null +++ b/os/lib/mk/ahci.inc @@ -0,0 +1,17 @@ +# +# \brief Generic toolchain configurations for AHCI +# \author Martin Stein +# \date 2013-05-17 +# + +# add C++ sources +SRC_CC += main.cc + +# add library dependencies +LIBS += base + +# add include directories +INC_DIR += $(REP_DIR)/src/drivers/ahci/include + +# declare source paths +vpath main.cc $(REP_DIR)/src/drivers/ahci diff --git a/os/lib/mk/x86_32/ahci.mk b/os/lib/mk/x86_32/ahci.mk new file mode 100644 index 000000000..9a06f4b02 --- /dev/null +++ b/os/lib/mk/x86_32/ahci.mk @@ -0,0 +1,11 @@ +# +# \brief Toolchain configurations for AHCI on X86 32 bit +# \author Martin Stein +# \date 2013-05-17 +# + +# add include directories +INC_DIR += $(REP_DIR)/src/drivers/ahci/x86_32 + +# include less specific config +include $(REP_DIR)/lib/mk/ahci.inc diff --git a/os/run/ahci.run b/os/run/ahci.run index 3d8f83ff2..b4ad127e4 100644 --- a/os/run/ahci.run +++ b/os/run/ahci.run @@ -1,17 +1,17 @@ -if {![have_spec x86_32]} { - puts "\nThe AHCI driver supports the x86_32 architecture only\n" +if {![have_spec x86_32] && ![have_spec exynos5]} { + puts "\nThe AHCI driver supports x86_32 architecture and exynos5 only\n" exit 0 } # # Build # -set build_components { - core init drivers/timer drivers/pci drivers/ahci test/block -} -lappend_if [have_spec acpi] build_components drivers/acpi -lappend_if [have_spec pci] build_components drivers/pci/device_pd +set build_components { core init drivers/timer drivers/ahci test/block } + +lappend_if [have_spec x86_32] build_components drivers/pci +lappend_if [have_spec acpi] build_components drivers/acpi +lappend_if [have_spec pci] build_components drivers/pci/device_pd build $build_components @@ -54,7 +54,7 @@ append_if [have_spec acpi] config { } -append_if [expr ![have_spec acpi]] config { +append_if [expr ![have_spec acpi] && [have_spec x86_32]] config { @@ -64,13 +64,17 @@ append config { - - + } + +append_if [have_spec acpi] config { + } + +append config { - + } @@ -97,13 +101,10 @@ install_config $config # Boot modules # -set boot_modules { - core init timer pci_drv ahci_drv test-block acpi_drv -} +set boot_modules { core init timer ahci test-block } -if {[have_spec nova]} { - append boot_modules {pci_device_pd} -} +append_if [have_spec x86_32] boot_modules { pci_drv acpi_drv } +append_if [have_spec nova] boot_modules pci_device_pd build_boot_image $boot_modules diff --git a/os/src/drivers/ahci/empty.cc b/os/src/drivers/ahci/empty.cc new file mode 100644 index 000000000..066c1bf82 --- /dev/null +++ b/os/src/drivers/ahci/empty.cc @@ -0,0 +1,3 @@ +/* + * Dummy compilation unit needed to link a valid target. + */ diff --git a/os/src/drivers/ahci/include/ahci_device_base.h b/os/src/drivers/ahci/include/ahci_device_base.h new file mode 100644 index 000000000..77631ff89 --- /dev/null +++ b/os/src/drivers/ahci/include/ahci_device_base.h @@ -0,0 +1,547 @@ +/* + * \brief Generic base of AHCI device + * \author Sebastian Sumpf + * \author Martin Stein + * \date 2011-08-10 + */ + +/* + * Copyright (C) 2011-2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _AHCI_DEVICE_BASE_H_ +#define _AHCI_DEVICE_BASE_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Enable for debugging output + */ +static const bool verbose = false; + +using namespace Genode; + + +/** + * Base class for register access + */ +class Reg_base +{ + protected: + + uint32_t _value(uint32_t offset) { return *(volatile uint32_t *)(this + offset); } + void _set(uint32_t offset, uint32_t val) { *(volatile uint32_t *)(this + offset) = val; } +}; + + +/* + * HBA: Generic Host Control + */ +class Generic_ctrl : public Reg_base +{ + public: + + /* host capabilities */ + uint32_t hba_cap() { return _value(0x0); } + + /* return port count from hba_cap */ + uint32_t port_count() { return (hba_cap() & 0x1f) + 1; } + + /* return command slot count from hba_cap */ + uint32_t cmd_slots() { return ((hba_cap() >> 8) & 0x1f) + 1; } + + /* global host control */ + uint32_t hba_ctrl() { return _value(0x4); } + void hba_ctrl(uint32_t val) { _set(0x4, val); } + + /* set Interrupt Enable (IE) in hba_ctrl */ + void global_interrupt_enable() + { + hba_ctrl(hba_ctrl() | 2); + + if (verbose) + PDBG("HBA %x", hba_ctrl()); + } + + /* set AHCI enable (AE) in hba_ctrl */ + void global_enable_ahci() + { + if (!(hba_ctrl() & (1 << 31))) + hba_ctrl(hba_ctrl() | (1 << 31)); + + if (verbose) + PDBG("AHCI ENABLED: %x", hba_ctrl()); + } + + /* global interrupt status (contains port interrupts) */ + uint32_t hba_intr_status() { return _value(0x8); } + void hba_intr_status(uint32_t val) { return _set(0x8, val); } + + /* acknowledge global interrupt */ + void hba_interrupt_ack() { hba_intr_status(hba_intr_status()); } + + /* AHCI version */ + uint32_t version() { return _value(0x10); } +}; + + +/* + * AHCI port registers (one set per port) + */ +class Ahci_port : public Reg_base +{ + public: + + /* command list base (lower 32 bit) */ + void cmd_list_base(addr_t cmd_base) { _set(0x0, cmd_base); } + + /* receive FIS base address (lower 32 bit) */ + void fis_base(addr_t fis_base) { _set(0x8, fis_base); } + + /* interrupt status */ + uint32_t intr_status() { return _value(0x10); } + void intr_status(uint32_t val) { _set(0x10, val); } + + /* interrupt enable */ + void intr_enable(uint32_t val) { _set(0x14, val); } + + /* command */ + uint32_t cmd() { return _value(0x18); } + void cmd(uint32_t val) { _set(0x18, val); } + + /* task file data */ + uint32_t tfd() { return _value(0x20); } + + /* Serial ATA status */ + uint32_t status() { return _value(0x28); } + + /* Serial ATA control */ + void sctl(uint32_t val) { _set(0x2c, val); } + uint32_t sctl() { return _value(0x2c); } + + /* Serial ATA error */ + void err(uint32_t val) { _set(0x30, val); } + uint32_t err() { return _value(0x30); } + + /* command issue (1 bit per command slot) */ + void cmd_issue(uint32_t val) { _set(0x38, val); } + uint32_t cmd_issue() { return _value(0x38); } + + /** + * Check if device is active + */ + bool status_active() + { + enum { + PRESENT_ESTABLISHED = 0x3, /* device is present and connection is established */ + PM_ACTIVE = 0x100, /* interface is in active power-mngmt. state */ + PM_PARTIRL = 0x200, /* interface is in partial power-mngmt. state */ + PM_SLUMBER = 0x600, /* interface is in slumber power-mngmt. state */ + }; + + uint32_t stat = status(); + uint32_t pm_stat = stat & 0xf00; + + /* if controller is in sleep state, try to wake up */ + if (pm_stat == PM_PARTIRL || pm_stat == PM_SLUMBER) { + + if (verbose) + PDBG("Controller is in sleep state, trying to wake up ..."); + + cmd(cmd() | (1 << 28)); + + while (!(stat & PM_ACTIVE) || (stat & 0xf) != PRESENT_ESTABLISHED) { stat = status(); } + } + return (((stat & 0xf) == PRESENT_ESTABLISHED) && (stat & PM_ACTIVE)); + } + + /** + * Enable CMD.ST to start command list processing + */ + void hba_enable() + { + enum { + STS_BSY = 0x80, /* device is busy */ + STS_DRQ = 0x08, /* data transfer requested */ + }; + + while (tfd() & (STS_BSY | STS_DRQ)) + if (verbose) + PDBG("TFD %x", tfd()); + + cmd(cmd() | 1); + } + + /** + * Disable CMD.ST + */ + void hba_disable() + { + if ((cmd() & 1) && !(cmd_issue() & 1)) + cmd(cmd() & ~1); + } + + /** + * Enable port interrupts + */ + void interrupt_enable() { intr_enable(~0U); } + + /** + * Acknowledge port interrupts + */ + uint32_t interrupt_ack() + { + interrupt_pm_ack(); + + uint32_t status = intr_status(); + intr_status(status); + return status; + } + + /** + * Check and handle interrupts due to power mgmt. state transitions + */ + void interrupt_pm_ack() + { + enum { + INT_PORT_CON_STATUS = 0x40, + INT_PHY_RDY_STATUS = 0x400000, + }; + uint32_t status = intr_status(); + + if (status & INT_PORT_CON_STATUS) + + /* clear DIAG.x */ + err(err() & ~(1 << 26)); + + if (status & INT_PORT_CON_STATUS) + + /* clear DIAG.n */ + err(err() & ~(1 << 16)); + } + + /** + * Disable power mgmt. set SCTL.IPM to 3 + */ + void disable_pm() { sctl(sctl() | (3 << 8)); } + + /** + * Power up device + */ + void get_ready() + { + enum { + SPIN_UP_DEVICE = 0x2, + POWER_ON_DEVICE = 0x4, + FIS_RECV_ENABLE = 0x10, + ENABLE = SPIN_UP_DEVICE | POWER_ON_DEVICE | FIS_RECV_ENABLE + }; + cmd(cmd() | ENABLE); + } + + /** + * Reset this port + */ + void reset() + { + /* check for ST bit in command register */ + if (cmd() & 1) + PWRN("CMD.ST bit set during device reset --> unknown behavior"); + + /* set device initialization bit for at least 1ms */ + sctl((sctl() & ~0xf) | 1); + + Timer::Connection timer; + timer.msleep(1); + + sctl(sctl() & ~0xf); + + while ((status() & 0xf) != 0x3) {} + } + + /** + * Return the size of this structure + */ + static uint32_t size() { return 0x80; } +}; + + +/** + * AHCI command list structure + */ +struct Command_list +{ + uint8_t cfl:5; /* Command FIS length */ + uint8_t a:1; /* ATAPI command flag */ + uint8_t w:1; /* Write flag */ + uint8_t p:1; /* Prefetchable flag */ + uint8_t unsused; /* we don't use byte 2 yet */ + uint16_t prdtl; /* Physical region descr. length */ + uint32_t prdbc; /* PRD byte count */ + uint32_t cmd_table_base_l; /* Command table base addr (low) */ + uint32_t cmd_table_base_u; + uint32_t reserved[0]; +}; + + +/** + * AHCI command table structure + */ +struct Command_table +{ + /** + * Setup FIS and PRD + */ + void setup_command(uint8_t cmd, uint32_t lba48, uint16_t blk_cnt, addr_t phys_addr) + { + enum { MAX_BYTES = 1 << 22 }; /* 4MB = one PRD */ + uint8_t *fis = (uint8_t *)this; + + /* setup FIS */ + fis[0] = 0x27; /* type = host to device */ + fis[1] = 0x80; /* set update command flag */ + fis[2] = cmd; /* actual command */ + fis[4] = lba48 & 0xff; /* LBA 0 - 7 */ + fis[5] = (lba48 >> 8) & 0xff; /* LBA 8 - 15 */ + fis[6] = (lba48 >> 16) & 0xff; /* LBA 16 - 23 */ + fis[7] = 0x40; /* LBA mode flag */ + fis[8] = (lba48 >> 24) & 0xff; /* LBA 24 - 31 */ + fis[9] = 0x0; /* LBA 32 - 39 */ + fis[10] = 0x0; /* LBA 40 - 47 */ + fis[12] = blk_cnt & 0xff; /* sector count 0 - 7 */ + fis[13] = (blk_cnt >> 8) & 0xff; /* sector count 8 - 15 */ + + /* setup PRD for DMA */ + memcpy(&fis[0x80], &phys_addr, 4); /* DBA: data base address */ + uint32_t bytes = (blk_cnt * 512) - 1; + + if (bytes + 1 > MAX_BYTES) { + PERR("Unsupported request size %u > %u", bytes, MAX_BYTES); + throw Block::Driver::Io_error(); + } + + /* set byte count for PRD 22 bit */ + fis[0x8c] = bytes & 0xff; + fis[0x8d] = (bytes >> 8) & 0xff; + fis[0x8e] = (bytes >> 16) & 0x3f; + } +}; + +/** + * Generic base of AHCI device + */ +class Ahci_device_base +{ + protected: + + enum { + AHCI_PORT_BASE = 0x100, + }; + + Generic_ctrl *_ctrl; /* generic host control */ + Ahci_port *_port; /* port base of device */ + Irq_connection *_irq; /* device IRQ */ + size_t _block_cnt; /* number of blocks on device */ + Command_list *_cmd_list; /* pointer to command list */ + Command_table *_cmd_table; /* pointer to command table */ + Ram_dataspace_capability _ds; /* backing-store of internal data structures */ + Io_mem_session_capability _io_cap; /* I/O mem cap */ + + /** + * Find first non-ATAPI device that is ready + */ + bool _scan_ports() + { + uint32_t port_cnt = _ctrl->port_count(); + + Ahci_port *port = (Ahci_port *)((char *)_ctrl + AHCI_PORT_BASE); + for (uint32_t i = 0; + i <= port_cnt; + i++, port = (Ahci_port *)((char *)port + Ahci_port::size())) { + + bool is_atapi = port->cmd() & (1 << 24); /* check bit 24 */ + PINF("Port %u: ATAPI %s", i, is_atapi ? "yes" : "no"); + + if (is_atapi) + continue; + + /* port status */ + if (port->status_active()) { + PINF("Port %u: Detected interface is active", i); + _port = port; + return true; + } + } + + return false; + } + + void _setup_memory() + { + _ds = alloc_dma_buffer(0x1000); + addr_t phys = Dataspace_client(_ds).phys_addr(); + uint8_t *virt = (uint8_t *)env()->rm_session()->attach(_ds); + + /* setup command list (size 1k naturally aligned) */ + _port->cmd_list_base(phys); + _cmd_list = (struct Command_list *)(virt); + + /* for now we transfer one PRD with a FIS size of 5 byte */ + _cmd_list->prdtl = 1; + _cmd_list->cfl = 5; + virt += 1024; phys += 1024; + + /* setup received FIS base (256 byte naturally aligned) */ + _port->fis_base(phys); + virt += 256; phys += 256; + + /* setup command table (128 byte aligned (cache line size)) */ + _cmd_list->cmd_table_base_l = phys; + _cmd_list->cmd_table_base_u = 0; + _cmd_table = (struct Command_table *)(virt); + } + + /** + * Execute a prepared command + */ + void _execute_command() + { + /* reset byte count */ + _cmd_list->prdbc = 0; + + /* start HBA command processing */ + _port->hba_enable(); + + if (verbose) + PDBG("Int status: global: %x port: %x error: %x", + _ctrl->hba_intr_status(), _port->intr_status(), _port->err()); + + /* write CI (command issue) slot 0 */ + _port->cmd_issue(1); + + /* wait for interrupt */ + _irq->wait_for_irq(); + + if (verbose) + PDBG("Int status (IRQ): global: %x port: %x error: %x", + _ctrl->hba_intr_status(), _port->intr_status(), _port->err()); + + /* acknowledge interrupt */ + uint32_t status = _port->interrupt_ack(); + + /* check for error */ + enum { + INT_SETUP_FIS_DMA = 0x4, + INT_SETUP_FIS_PIO = 0x2, + INT_HOST_REGISTER_FIS = 0x1, + INT_OK = INT_SETUP_FIS_DMA | INT_SETUP_FIS_PIO | INT_HOST_REGISTER_FIS + }; + + if (!(status & INT_OK)) { + PERR("Error during SATA request (irq state %x)", status); + throw Block::Driver::Io_error(); + } + + /* acknowledge global port interrupt */ + _ctrl->hba_interrupt_ack(); + + /* disable hba */ + _port->hba_disable(); + } + + /** + * Execute ATA 'IDENTIFY DEVICE' command + */ + void _identify_device() + { + Ram_dataspace_capability ds = alloc_dma_buffer(0x1000); + uint16_t *dev_info = (uint16_t *)env()->rm_session()->attach(ds); + + enum { IDENTIFY_DEVICE = 0xec }; + try { + addr_t phys = Dataspace_client(ds).phys_addr(); + _cmd_table->setup_command(IDENTIFY_DEVICE, 0, 0, phys); + _execute_command(); + + /* XXX: just read 32 bit for now */ + _block_cnt = *((size_t *)&dev_info[100]); + + } catch (Block::Driver::Io_error) { + PERR("I/O Error: Could not identify device"); + } + + if (verbose) + PDBG("Max LBA48 block: %zu", _block_cnt); + + env()->rm_session()->detach(dev_info); + env()->ram_session()->free(ds); + } + + Ahci_device_base(addr_t base, Io_mem_session_capability io_cap) + : _ctrl((Generic_ctrl *)base), _port(0), _irq(0), _cmd_list(0), + _cmd_table(0), _io_cap(io_cap) { } + + public: + + virtual ~Ahci_device_base() + { + /* delete internal data structures */ + if (_ds.valid()) { + env()->rm_session()->detach((void*)_cmd_list); + env()->ram_session()->free(_ds); + } + + /* close I/O mem session */ + env()->rm_session()->detach((void *)_ctrl); + env()->parent()->close(_io_cap); + + /* XXX release _pci_device */ + + /* close IRQ session */ + destroy(env()->heap(), _irq); + } + + static size_t block_size() { return 512; } + size_t block_count() { return _block_cnt; } + + /** + * Issue ATA 'READ_DMA_EXT' command + */ + void read(size_t block_number, size_t block_count, addr_t phys) + { + _cmd_list->w = 0; + + enum { READ_DMA_EXT = 0x25 }; + _cmd_table->setup_command(READ_DMA_EXT, block_number, block_count, phys); + _execute_command(); + } + + /** + * Issue ATA 'WRITE_DMA_EXT' command + */ + void write(size_t block_number, size_t block_count, addr_t phys) + { + _cmd_list->w = 1; + + enum { WRITE_DMA_EXT = 0x35 }; + _cmd_table->setup_command(WRITE_DMA_EXT, block_number, block_count, phys); + _execute_command(); + } + + virtual Ram_dataspace_capability alloc_dma_buffer(size_t size) = 0; +}; + +#endif /* _AHCI_DEVICE_BASE_H_ */ + diff --git a/os/src/drivers/ahci/include/ahci_driver_base.h b/os/src/drivers/ahci/include/ahci_driver_base.h new file mode 100644 index 000000000..2a5e47eab --- /dev/null +++ b/os/src/drivers/ahci/include/ahci_driver_base.h @@ -0,0 +1,84 @@ + +/* + * \brief Generic base of AHCI driver + * \author Sebastian Sumpf + * \author Martin Stein + * \date 2011-08-10 + */ + +/* + * Copyright (C) 2011-2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _AHCI_DRIVER_BASE_H_ +#define _AHCI_DRIVER_BASE_H_ + +/* local includes */ +#include + +/** + * Implementation of block driver interface + */ +class Ahci_driver_base : public Block::Driver +{ + protected: + + Ahci_device *_device; + + void _sanity_check(size_t block_number, size_t count) + { + if (!_device || (block_number + count > block_count())) + throw Io_error(); + } + + Ahci_driver_base(Ahci_device * const device) : _device(device) { } + + public: + + ~Ahci_driver_base() + { + if (_device) + destroy(env()->heap(), _device); + } + + size_t block_size() { return Ahci_device::block_size(); } + size_t block_count() { return _device ? _device->block_count() : 0; } + bool dma_enabled() { return true; } + + void read_dma(size_t block_number, + size_t block_count, + addr_t phys) + { + _sanity_check(block_number, block_count); + _device->read(block_number, block_count, phys); + } + + void write_dma(size_t block_number, + size_t block_count, + addr_t phys) + { + _sanity_check(block_number, block_count); + _device->write(block_number, block_count, phys); + } + + void read(size_t, size_t, char *) + { + PERR("%s should not be called", __PRETTY_FUNCTION__); + throw Io_error(); + } + + void write(size_t, size_t, char const *) + { + PERR("%s should not be called", __PRETTY_FUNCTION__); + throw Io_error(); + } + + Ram_dataspace_capability alloc_dma_buffer(size_t size) { + return _device->alloc_dma_buffer(size); } +}; + +#endif /* _AHCI_DRIVER_BASE_H_ */ + diff --git a/os/src/drivers/ahci/main.cc b/os/src/drivers/ahci/main.cc index 9b29835f9..45c54286f 100644 --- a/os/src/drivers/ahci/main.cc +++ b/os/src/drivers/ahci/main.cc @@ -16,769 +16,16 @@ * under the terms of the GNU General Public License version 2. */ -#include -#include -#include -#include +/* Genode includes */ #include -#include -#include -#include -#include -#include -#include +#include +#include - -/** - * Enable for debugging output - */ -static const bool verbose = false; +/* local includes */ +#include using namespace Genode; - -/** - * Base class for register access - */ -class Reg_base -{ - protected: - - uint32_t _value(uint32_t offset) { return *(volatile uint32_t *)(this + offset); } - void _set(uint32_t offset, uint32_t val) { *(volatile uint32_t *)(this + offset) = val; } -}; - - -/* - * HBA: Generic Host Control - */ -class Generic_ctrl : public Reg_base -{ - public: - - /* host capabilities */ - uint32_t hba_cap() { return _value(0x0); } - - /* return port count from hba_cap */ - uint32_t port_count() { return (hba_cap() & 0x1f) + 1; } - - /* return command slot count from hba_cap */ - uint32_t cmd_slots() { return ((hba_cap() >> 8) & 0x1f) + 1; } - - /* global host control */ - uint32_t hba_ctrl() { return _value(0x4); } - void hba_ctrl(uint32_t val) { _set(0x4, val); } - - /* set Interrupt Enable (IE) in hba_ctrl */ - void global_interrupt_enable() - { - hba_ctrl(hba_ctrl() | 2); - - if (verbose) - PDBG("HBA %x", hba_ctrl()); - } - - /* set AHCI enable (AE) in hba_ctrl */ - void global_enable_ahci() - { - if (!(hba_ctrl() & (1 << 31))) - hba_ctrl(hba_ctrl() | (1 << 31)); - - if (verbose) - PDBG("AHCI ENABLED: %x", hba_ctrl()); - } - - /* global interrupt status (contains port interrupts) */ - uint32_t hba_intr_status() { return _value(0x8); } - void hba_intr_status(uint32_t val) { return _set(0x8, val); } - - /* acknowledge global interrupt */ - void hba_interrupt_ack() { hba_intr_status(hba_intr_status()); } - - /* AHCI version */ - uint32_t version() { return _value(0x10); } -}; - - -/* - * AHCI port registers (one set per port) - */ -class Ahci_port : public Reg_base -{ - public: - - /* command list base (lower 32 bit) */ - void cmd_list_base(addr_t cmd_base) { _set(0x0, cmd_base); } - - /* receive FIS base address (lower 32 bit) */ - void fis_base(addr_t fis_base) { _set(0x8, fis_base); } - - /* interrupt status */ - uint32_t intr_status() { return _value(0x10); } - void intr_status(uint32_t val) { _set(0x10, val); } - - /* interrupt enable */ - void intr_enable(uint32_t val) { _set(0x14, val); } - - /* command */ - uint32_t cmd() { return _value(0x18); } - void cmd(uint32_t val) { _set(0x18, val); } - - /* task file data */ - uint32_t tfd() { return _value(0x20); } - - /* Serial ATA status */ - uint32_t status() { return _value(0x28); } - - /* Serial ATA control */ - void sctl(uint32_t val) { _set(0x2c, val); } - uint32_t sctl() { return _value(0x2c); } - - /* Serial ATA error */ - void err(uint32_t val) { _set(0x30, val); } - uint32_t err() { return _value(0x30); } - - /* command issue (1 bit per command slot) */ - void cmd_issue(uint32_t val) { _set(0x38, val); } - uint32_t cmd_issue() { return _value(0x38); } - - /** - * Check if device is active - */ - bool status_active() - { - enum { - PRESENT_ESTABLISHED = 0x3, /* device is present and connection is established */ - PM_ACTIVE = 0x100, /* interface is in active power-mngmt. state */ - PM_PARTIRL = 0x200, /* interface is in partial power-mngmt. state */ - PM_SLUMBER = 0x600, /* interface is in slumber power-mngmt. state */ - }; - - uint32_t stat = status(); - uint32_t pm_stat = stat & 0xf00; - - /* if controller is in sleep state, try to wake up */ - if (pm_stat == PM_PARTIRL || pm_stat == PM_SLUMBER) { - - if (verbose) - PDBG("Controller is in sleep state, trying to wake up ..."); - - cmd(cmd() | (1 << 28)); - - while (!(stat & PM_ACTIVE) || (stat & 0xf) != PRESENT_ESTABLISHED) { stat = status(); } - } - return (((stat & 0xf) == PRESENT_ESTABLISHED) && (stat & PM_ACTIVE)); - } - - /** - * Enable CMD.ST to start command list processing - */ - void hba_enable() - { - enum { - STS_BSY = 0x80, /* device is busy */ - STS_DRQ = 0x08, /* data transfer requested */ - }; - - while (tfd() & (STS_BSY | STS_DRQ)) - if (verbose) - PDBG("TFD %x", tfd()); - - cmd(cmd() | 1); - } - - /** - * Disable CMD.ST - */ - void hba_disable() - { - if ((cmd() & 1) && !(cmd_issue() & 1)) - cmd(cmd() & ~1); - } - - /** - * Enable port interrupts - */ - void interrupt_enable() { intr_enable(~0U); } - - /** - * Acknowledge port interrupts - */ - uint32_t interrupt_ack() - { - interrupt_pm_ack(); - - uint32_t status = intr_status(); - intr_status(status); - return status; - } - - /** - * Check and handle interrupts due to power mgmt. state transitions - */ - void interrupt_pm_ack() - { - enum { - INT_PORT_CON_STATUS = 0x40, - INT_PHY_RDY_STATUS = 0x400000, - }; - uint32_t status = intr_status(); - - if (status & INT_PORT_CON_STATUS) - - /* clear DIAG.x */ - err(err() & ~(1 << 26)); - - if (status & INT_PORT_CON_STATUS) - - /* clear DIAG.n */ - err(err() & ~(1 << 16)); - } - - /** - * Disable power mgmt. set SCTL.IPM to 3 - */ - void disable_pm() { sctl(sctl() | (3 << 8)); } - - /** - * Power up device - */ - void get_ready() - { - enum { - SPIN_UP_DEVICE = 0x2, - POWER_ON_DEVICE = 0x4, - FIS_RECV_ENABLE = 0x10, - ENABLE = SPIN_UP_DEVICE | POWER_ON_DEVICE | FIS_RECV_ENABLE - }; - cmd(cmd() | ENABLE); - } - - /** - * Reset this port - */ - void reset() - { - /* check for ST bit in command register */ - if (cmd() & 1) - PWRN("CMD.ST bit set during device reset --> unknown behavior"); - - /* set device initialization bit for at least 1ms */ - sctl((sctl() & ~0xf) | 1); - - Timer::Connection timer; - timer.msleep(1); - - sctl(sctl() & ~0xf); - - while ((status() & 0xf) != 0x3) {} - } - - /** - * Return the size of this structure - */ - static uint32_t size() { return 0x80; } -}; - - -/** - * AHCI command list structure - */ -struct Command_list -{ - uint8_t cfl:5; /* Command FIS length */ - uint8_t a:1; /* ATAPI command flag */ - uint8_t w:1; /* Write flag */ - uint8_t p:1; /* Prefetchable flag */ - uint8_t unsused; /* we don't use byte 2 yet */ - uint16_t prdtl; /* Physical region descr. length */ - uint32_t prdbc; /* PRD byte count */ - uint32_t cmd_table_base_l; /* Command table base addr (low) */ - uint32_t cmd_table_base_u; - uint32_t reserved[0]; -}; - - -/** - * AHCI command table structure - */ -struct Command_table -{ - /** - * Setup FIS and PRD - */ - void setup_command(uint8_t cmd, uint32_t lba48, uint16_t blk_cnt, addr_t phys_addr) - { - enum { MAX_BYTES = 1 << 22 }; /* 4MB = one PRD */ - uint8_t *fis = (uint8_t *)this; - - /* setup FIS */ - fis[0] = 0x27; /* type = host to device */ - fis[1] = 0x80; /* set update command flag */ - fis[2] = cmd; /* actual command */ - fis[4] = lba48 & 0xff; /* LBA 0 - 7 */ - fis[5] = (lba48 >> 8) & 0xff; /* LBA 8 - 15 */ - fis[6] = (lba48 >> 16) & 0xff; /* LBA 16 - 23 */ - fis[7] = 0x40; /* LBA mode flag */ - fis[8] = (lba48 >> 24) & 0xff; /* LBA 24 - 31 */ - fis[9] = 0x0; /* LBA 32 - 39 */ - fis[10] = 0x0; /* LBA 40 - 47 */ - fis[12] = blk_cnt & 0xff; /* sector count 0 - 7 */ - fis[13] = (blk_cnt >> 8) & 0xff; /* sector count 8 - 15 */ - - /* setup PRD for DMA */ - memcpy(&fis[0x80], &phys_addr, 4); /* DBA: data base address */ - uint32_t bytes = (blk_cnt * 512) - 1; - - if (bytes + 1 > MAX_BYTES) { - PERR("Unsupported request size %u > %u", bytes, MAX_BYTES); - throw Block::Driver::Io_error(); - } - - /* set byte count for PRD 22 bit */ - fis[0x8c] = bytes & 0xff; - fis[0x8d] = (bytes >> 8) & 0xff; - fis[0x8e] = (bytes >> 16) & 0x3f; - } -}; - - -/** - * AHCI device - */ -class Ahci_device -{ - private: - - enum Pci_config { - PCI_CFG_BMIBA_OFF = 0x24, /* offset in PCI config space */ - CLASS_MASS_STORAGE = 0x10000U, - SUBCLASS_AHCI = 0x0600U, - CLASS_MASK = 0xffff00U, - AHCI_BASE_ID = 0x5, /* resource id of AHCI base addr */ - AHCI_INTR_OFF = 0x3c, /* offset of interrupt information in config space */ - AHCI_PORT_BASE = 0x100, - }; - - Generic_ctrl *_ctrl; /* generic host control */ - Ahci_port *_port; /* port base of device */ - Irq_connection *_irq; /* device IRQ */ - size_t _block_cnt; /* number of blocks on device */ - Command_list *_cmd_list; /* pointer to command list */ - Command_table *_cmd_table; /* pointer to command table */ - Ram_dataspace_capability _ds; /* backing-store of internal data structures */ - Io_mem_session_capability _io_cap; /* I/O mem cap */ - - ::Pci::Connection &_pci; - ::Pci::Device_client *_pci_device; - ::Pci::Device_capability _pci_device_cap; - - /** - * Return next PCI device - */ - static Pci::Device_capability _scan_pci(Pci::Connection &pci, Pci::Device_capability prev_device_cap) - { - Pci::Device_capability device_cap; - device_cap = pci.next_device(prev_device_cap, - CLASS_MASS_STORAGE | SUBCLASS_AHCI, - CLASS_MASK); - - if (prev_device_cap.valid()) - pci.release_device(prev_device_cap); - - return device_cap; - } - - /** - * Find first non-ATAPI device that is ready - */ - bool _scan_ports() - { - uint32_t port_cnt = _ctrl->port_count(); - - Ahci_port *port = (Ahci_port *)((char *)_ctrl + AHCI_PORT_BASE); - for (uint32_t i = 0; - i <= port_cnt; - i++, port = (Ahci_port *)((char *)port + Ahci_port::size())) { - - bool is_atapi = port->cmd() & (1 << 24); /* check bit 24 */ - PINF("Port %u: ATAPI %s", i, is_atapi ? "yes" : "no"); - - if (is_atapi) - continue; - - /* port status */ - if (port->status_active()) { - PINF("Port %u: Detected interface is active", i); - _port = port; - return true; - } - } - - return false; - } - - /** - * Initialize port - */ - void _init(::Pci::Device_client *pci_device) - { - uint32_t version = _ctrl->version(); - PINF("AHCI Version: %x.%04x", (version >> 16), version && 0xffff); - - /* HBA capabilities at offset 0 */ - uint32_t caps = _ctrl->hba_cap(); - PINF("CAPs:"); - PINF("\tPort count: %u", _ctrl->port_count()); - PINF("\tCommand slots: %u", _ctrl->cmd_slots()); - PINF("\tAHCI only: %s", (caps & 0x20000) ? "yes" : "no"); - PINF("\tNative command queuing: %s", (caps & 0x40000000) ? "yes" : "no"); - PINF("\t64 Bit: %s", (caps & 0x80000000) ? "yes" : "no"); - - /* setup up AHCI data structures */ - _setup_memory(pci_device); - - /* check and possibly enable AHCI mode */ - _ctrl->global_enable_ahci(); - - /* enable global interrupts */ - _ctrl->global_interrupt_enable(); - - /* disable power mgmt. */ - _port->disable_pm(); - - /* startup device */ - _port->get_ready(); - - /* reset port */ - _port->reset(); - - /* clear error register */ - _port->err(_port->err()); - - /* port interrupt enable */ - _port->interrupt_enable(); - - /* ack all possibly pending interrupts */ - _port->interrupt_ack(); - _ctrl->hba_interrupt_ack(); - - /* retrieve block count */ - _identify_device(pci_device); - } - - void _setup_memory(::Pci::Device_client *pci_device) - { - _ds = alloc_dma_buffer(0x1000); - addr_t phys = Dataspace_client(_ds).phys_addr(); - uint8_t *virt = (uint8_t *)env()->rm_session()->attach(_ds); - - /* setup command list (size 1k naturally aligned) */ - _port->cmd_list_base(phys); - _cmd_list = (struct Command_list *)(virt); - - /* for now we transfer one PRD with a FIS size of 5 byte */ - _cmd_list->prdtl = 1; - _cmd_list->cfl = 5; - virt += 1024; phys += 1024; - - /* setup received FIS base (256 byte naturally aligned) */ - _port->fis_base(phys); - virt += 256; phys += 256; - - /* setup command table (128 byte aligned (cache line size)) */ - _cmd_list->cmd_table_base_l = phys; - _cmd_list->cmd_table_base_u = 0; - _cmd_table = (struct Command_table *)(virt); - } - - /** - * Execute ATA 'IDENTIFY DEVICE' command - */ - void _identify_device(Pci::Device_client *pci_device) - { - Ram_dataspace_capability ds = alloc_dma_buffer(0x1000); - uint16_t *dev_info = (uint16_t *)env()->rm_session()->attach(ds); - - enum { IDENTIFY_DEVICE = 0xec }; - try { - addr_t phys = Dataspace_client(ds).phys_addr(); - _cmd_table->setup_command(IDENTIFY_DEVICE, 0, 0, phys); - _execute_command(); - - /* XXX: just read 32 bit for now */ - _block_cnt = *((size_t *)&dev_info[100]); - - } catch (Block::Driver::Io_error) { - PERR("I/O Error: Could not identify device"); - } - - if (verbose) - PDBG("Max LBA48 block: %zu", _block_cnt); - - env()->rm_session()->detach(dev_info); - env()->ram_session()->free(ds); - } - - /** - * Execute a prepared command - */ - void _execute_command() - { - /* reset byte count */ - _cmd_list->prdbc = 0; - - /* start HBA command processing */ - _port->hba_enable(); - - if (verbose) - PDBG("Int status: global: %x port: %x error: %x", - _ctrl->hba_intr_status(), _port->intr_status(), _port->err()); - - /* write CI (command issue) slot 0 */ - _port->cmd_issue(1); - - /* wait for interrupt */ - _irq->wait_for_irq(); - - if (verbose) - PDBG("Int status (IRQ): global: %x port: %x error: %x", - _ctrl->hba_intr_status(), _port->intr_status(), _port->err()); - - /* acknowledge interrupt */ - uint32_t status = _port->interrupt_ack(); - - /* check for error */ - enum { - INT_SETUP_FIS_DMA = 0x4, - INT_SETUP_FIS_PIO = 0x2, - INT_HOST_REGISTER_FIS = 0x1, - INT_OK = INT_SETUP_FIS_DMA | INT_SETUP_FIS_PIO | INT_HOST_REGISTER_FIS - }; - - if (!(status & INT_OK)) { - PERR("Error during SATA request (irq state %x)", status); - throw Block::Driver::Io_error(); - } - - /* acknowledge global port interrupt */ - _ctrl->hba_interrupt_ack(); - - /* disable hba */ - _port->hba_disable(); - } - - static void _disable_msi(::Pci::Device_client *pci) - { - enum { PM_CAP_OFF = 0x34, MSI_CAP = 0x5, MSI_ENABLED = 0x1 }; - uint8_t cap = pci->config_read(PM_CAP_OFF, ::Pci::Device::ACCESS_8BIT); - - /* iterate through cap pointers */ - for (uint16_t val = 0; cap; cap = val >> 8) { - val = pci->config_read(cap, ::Pci::Device::ACCESS_16BIT); - - if ((val & 0xff) != MSI_CAP) - continue; - uint16_t msi = pci->config_read(cap + 2, ::Pci::Device::ACCESS_16BIT); - - if (msi & MSI_ENABLED) { - pci->config_write(cap + 2, msi ^ MSI_CAP, ::Pci::Device::ACCESS_8BIT); - PINF("Disabled MSIs %x", msi); - } - } - } - - public: - - Ahci_device(addr_t base, Io_mem_session_capability io_cap, - Pci::Connection &pci) - : - _ctrl((Generic_ctrl *)base), _port(0), _irq(0), _cmd_list(0), - _cmd_table(0), _io_cap(io_cap), _pci(pci), _pci_device(0) {} - - ~Ahci_device() - { - /* delete internal data structures */ - if (_ds.valid()) { - env()->rm_session()->detach((void*)_cmd_list); - env()->ram_session()->free(_ds); - } - - /* close I/O mem session */ - env()->rm_session()->detach((void *)_ctrl); - env()->parent()->close(_io_cap); - - /* XXX release _pci_device */ - - /* close IRQ session */ - destroy(env()->heap(), _irq); - } - - /** - * Probe PCI-bus for AHCI and ATA-devices - */ - static Ahci_device *probe(Pci::Connection &pci) - { - Pci::Device_capability device_cap; - Ahci_device *device; - - while ((device_cap = _scan_pci(pci, device_cap)).valid()) { - - ::Pci::Device_client * pci_device = - new(env()->heap()) ::Pci::Device_client(device_cap); - - PINF("Found AHCI HBA (Vendor ID: %04x Device ID: %04x Class:" - " %08x)\n", pci_device->vendor_id(), - pci_device->device_id(), pci_device->class_code()); - - /* read and map base address of AHCI controller */ - Pci::Device::Resource resource = pci_device->resource(AHCI_BASE_ID); - - Io_mem_connection io(resource.base(), resource.size()); - addr_t addr = (addr_t)env()->rm_session()->attach(io.dataspace()); - - /* add possible resource offset */ - addr += resource.base() & 0xfff; - - if (verbose) - PDBG("resource base: %x virt: %lx", resource.base(), addr); - - /* create and test device */ - device = new(env()->heap()) Ahci_device(addr, io.cap(), pci); - if (device->_scan_ports()) { - - io.on_destruction(Io_mem_connection::KEEP_OPEN); - - /* read IRQ information */ - unsigned long intr = pci_device->config_read(AHCI_INTR_OFF, - ::Pci::Device::ACCESS_32BIT); - - if (verbose) { - PDBG("Interrupt pin: %lu line: %lu", (intr >> 8) & 0xff, intr & 0xff); - - unsigned char bus, dev, func; - pci_device->bus_address(&bus, &dev, &func); - PDBG("Bus address: %x:%02x.%u (0x%x)", bus, dev, func, (bus << 8) | ((dev & 0x1f) << 3) | (func & 0x7)); - } - - /* disable message signaled interrupts */ - _disable_msi(pci_device); - - device->_irq = new(env()->heap()) Irq_connection(intr & 0xff); - /* remember pci_device to be able to allocate ram memory which is dma able */ - device->_pci_device_cap = device_cap; - device->_pci_device = pci_device; - /* trigger assignment of pci device to the ahci driver */ - pci.config_extended(device_cap); - - /* get device ready */ - device->_init(pci_device); - - return device; - } - - destroy(env()->heap(), pci_device); - env()->rm_session()->detach(addr); - destroy(env()->heap(), device); - } - return 0; - } - - static size_t block_size() { return 512; } - size_t block_count() { return _block_cnt; } - - /** - * Issue ATA 'READ_DMA_EXT' command - */ - void read(size_t block_number, size_t block_count, addr_t phys) - { - _cmd_list->w = 0; - - enum { READ_DMA_EXT = 0x25 }; - _cmd_table->setup_command(READ_DMA_EXT, block_number, block_count, phys); - _execute_command(); - } - - /** - * Issue ATA 'WRITE_DMA_EXT' command - */ - void write(size_t block_number, size_t block_count, addr_t phys) - { - _cmd_list->w = 1; - - enum { WRITE_DMA_EXT = 0x35 }; - _cmd_table->setup_command(WRITE_DMA_EXT, block_number, block_count, phys); - _execute_command(); - } - - Ram_dataspace_capability alloc_dma_buffer(size_t size) { - return _pci.alloc_dma_buffer(_pci_device_cap, size); } -}; - - -/** - * Implementation of block driver interface - */ -class Ahci_driver : public Block::Driver -{ - private: - - Pci::Connection _pci; - Ahci_device *_device; - - void _sanity_check(size_t block_number, size_t count) - { - if (!_device || (block_number + count > block_count())) - throw Io_error(); - } - - public: - - Ahci_driver() : _device(Ahci_device::probe(_pci)) { } - - ~Ahci_driver() - { - if (_device) - destroy(env()->heap(), _device); - } - - size_t block_size() { return Ahci_device::block_size(); } - size_t block_count() { return _device ? _device->block_count() : 0; } - bool dma_enabled() { return true; } - - void read_dma(size_t block_number, - size_t block_count, - addr_t phys) - { - _sanity_check(block_number, block_count); - _device->read(block_number, block_count, phys); - } - - void write_dma(size_t block_number, - size_t block_count, - addr_t phys) - { - _sanity_check(block_number, block_count); - _device->write(block_number, block_count, phys); - } - - void read(size_t, size_t, char *) - { - PERR("%s should not be called", __PRETTY_FUNCTION__); - throw Io_error(); - } - - void write(size_t, size_t, char const *) - { - PERR("%s should not be called", __PRETTY_FUNCTION__); - throw Io_error(); - } - - Ram_dataspace_capability alloc_dma_buffer(size_t size) { - return _device->alloc_dma_buffer(size); } -}; - - int main() { printf("--- AHCI driver started ---\n"); @@ -792,7 +39,7 @@ int main() Genode::destroy(env()->heap(), static_cast(driver)); } } driver_factory; - enum { STACK_SIZE = 8129 }; + enum { STACK_SIZE = 8128 }; static Cap_connection cap; static Rpc_entrypoint ep(&cap, STACK_SIZE, "block_ep"); @@ -802,3 +49,4 @@ int main() sleep_forever(); return 0; } + diff --git a/os/src/drivers/ahci/target.mk b/os/src/drivers/ahci/target.mk index db519c9f5..0b75663e8 100644 --- a/os/src/drivers/ahci/target.mk +++ b/os/src/drivers/ahci/target.mk @@ -1,5 +1,3 @@ -TARGET = ahci_drv -REQUIRES = x86_32 -SRC_CC = main.cc -LIBS = base - +TARGET = ahci +LIBS += ahci +SRC_CC += empty.cc diff --git a/os/src/drivers/ahci/x86_32/ahci_device.h b/os/src/drivers/ahci/x86_32/ahci_device.h new file mode 100644 index 000000000..9bb3928f2 --- /dev/null +++ b/os/src/drivers/ahci/x86_32/ahci_device.h @@ -0,0 +1,211 @@ +/* + * \brief AHCI device + * \author Sebastian Sumpf + * \author Martin Stein + * \date 2011-08-10 + */ + +/* + * Copyright (C) 2011-2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _AHCI_DEVICE_H_ +#define _AHCI_DEVICE_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include + +/** + * AHCI device + */ +class Ahci_device : public Ahci_device_base +{ + private: + + enum Pci_config { + PCI_CFG_BMIBA_OFF = 0x24, /* offset in PCI config space */ + CLASS_MASS_STORAGE = 0x10000U, + SUBCLASS_AHCI = 0x0600U, + CLASS_MASK = 0xffff00U, + AHCI_BASE_ID = 0x5, /* resource id of AHCI base addr */ + AHCI_INTR_OFF = 0x3c, /* offset of interrupt information in config space */ + }; + + ::Pci::Connection &_pci; + ::Pci::Device_client *_pci_device; + ::Pci::Device_capability _pci_device_cap; + + /** + * Return next PCI device + */ + static Pci::Device_capability _scan_pci(Pci::Connection &pci, Pci::Device_capability prev_device_cap) + { + Pci::Device_capability device_cap; + device_cap = pci.next_device(prev_device_cap, + CLASS_MASS_STORAGE | SUBCLASS_AHCI, + CLASS_MASK); + + if (prev_device_cap.valid()) + pci.release_device(prev_device_cap); + + return device_cap; + } + + /** + * Initialize port + */ + void _init(::Pci::Device_client *pci_device) + { + uint32_t version = _ctrl->version(); + PINF("AHCI Version: %x.%04x", (version >> 16), version & 0xffff); + + /* HBA capabilities at offset 0 */ + uint32_t caps = _ctrl->hba_cap(); + PINF("CAPs:"); + PINF("\tPort count: %u", _ctrl->port_count()); + PINF("\tCommand slots: %u", _ctrl->cmd_slots()); + PINF("\tAHCI only: %s", (caps & 0x20000) ? "yes" : "no"); + PINF("\tNative command queuing: %s", (caps & 0x40000000) ? "yes" : "no"); + PINF("\t64 Bit: %s", (caps & 0x80000000) ? "yes" : "no"); + + /* setup up AHCI data structures */ + _setup_memory(); + + /* check and possibly enable AHCI mode */ + _ctrl->global_enable_ahci(); + + /* enable global interrupts */ + _ctrl->global_interrupt_enable(); + + /* disable power mgmt. */ + _port->disable_pm(); + + /* startup device */ + _port->get_ready(); + + /* reset port */ + _port->reset(); + + /* clear error register */ + _port->err(_port->err()); + + /* port interrupt enable */ + _port->interrupt_enable(); + + /* ack all possibly pending interrupts */ + _port->interrupt_ack(); + _ctrl->hba_interrupt_ack(); + + /* retrieve block count */ + _identify_device(); + } + + static void _disable_msi(::Pci::Device_client *pci) + { + enum { PM_CAP_OFF = 0x34, MSI_CAP = 0x5, MSI_ENABLED = 0x1 }; + uint8_t cap = pci->config_read(PM_CAP_OFF, ::Pci::Device::ACCESS_8BIT); + + /* iterate through cap pointers */ + for (uint16_t val = 0; cap; cap = val >> 8) { + val = pci->config_read(cap, ::Pci::Device::ACCESS_16BIT); + + if ((val & 0xff) != MSI_CAP) + continue; + uint16_t msi = pci->config_read(cap + 2, ::Pci::Device::ACCESS_16BIT); + + if (msi & MSI_ENABLED) { + pci->config_write(cap + 2, msi ^ MSI_CAP, ::Pci::Device::ACCESS_8BIT); + PINF("Disabled MSIs %x", msi); + } + } + } + + public: + + Ahci_device(addr_t base, Io_mem_session_capability io_cap, + Pci::Connection &pci) + : Ahci_device_base(base, io_cap), _pci(pci), _pci_device(0) { } + + /** + * Probe PCI-bus for AHCI and ATA-devices + */ + static Ahci_device *probe(Pci::Connection &pci) + { + Pci::Device_capability device_cap; + Ahci_device *device; + + while ((device_cap = _scan_pci(pci, device_cap)).valid()) { + + ::Pci::Device_client * pci_device = + new(env()->heap()) ::Pci::Device_client(device_cap); + + PINF("Found AHCI HBA (Vendor ID: %04x Device ID: %04x Class:" + " %08x)\n", pci_device->vendor_id(), + pci_device->device_id(), pci_device->class_code()); + + /* read and map base address of AHCI controller */ + Pci::Device::Resource resource = pci_device->resource(AHCI_BASE_ID); + + Io_mem_connection io(resource.base(), resource.size()); + addr_t addr = (addr_t)env()->rm_session()->attach(io.dataspace()); + + /* add possible resource offset */ + addr += resource.base() & 0xfff; + + if (verbose) + PDBG("resource base: %x virt: %lx", resource.base(), addr); + + /* create and test device */ + device = new(env()->heap()) Ahci_device(addr, io.cap(), pci); + if (device->_scan_ports()) { + + io.on_destruction(Io_mem_connection::KEEP_OPEN); + + /* read IRQ information */ + unsigned long intr = pci_device->config_read(AHCI_INTR_OFF, + ::Pci::Device::ACCESS_32BIT); + + if (verbose) { + PDBG("Interrupt pin: %lu line: %lu", (intr >> 8) & 0xff, intr & 0xff); + + unsigned char bus, dev, func; + pci_device->bus_address(&bus, &dev, &func); + PDBG("Bus address: %x:%02x.%u (0x%x)", bus, dev, func, (bus << 8) | ((dev & 0x1f) << 3) | (func & 0x7)); + } + + /* disable message signaled interrupts */ + _disable_msi(pci_device); + + device->_irq = new(env()->heap()) Irq_connection(intr & 0xff); + /* remember pci_device to be able to allocate ram memory which is dma able */ + device->_pci_device_cap = device_cap; + device->_pci_device = pci_device; + /* trigger assignment of pci device to the ahci driver */ + pci.config_extended(device_cap); + + /* get device ready */ + device->_init(pci_device); + + return device; + } + + destroy(env()->heap(), pci_device); + env()->rm_session()->detach(addr); + destroy(env()->heap(), device); + } + return 0; + } + + Ram_dataspace_capability alloc_dma_buffer(size_t size) { + return _pci.alloc_dma_buffer(_pci_device_cap, size); } +}; + +#endif /* _AHCI_DEVICE_H_ */ + diff --git a/os/src/drivers/ahci/x86_32/ahci_driver.h b/os/src/drivers/ahci/x86_32/ahci_driver.h new file mode 100644 index 000000000..105a48d6c --- /dev/null +++ b/os/src/drivers/ahci/x86_32/ahci_driver.h @@ -0,0 +1,49 @@ +/* + * \brief AHCI driver + * \author Sebastian Sumpf + * \author Martin Stein + * \date 2006-08-15 + */ + +/* + * Copyright (C) 2006-2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _AHCI_DRIVER_H_ +#define _AHCI_DRIVER_H_ + +/* Genode includes */ +#include + +/* local includes */ +#include + +/** + * Helper class for AHCI driver to ensure specific member-initialization order + */ +class Ahci_pci_connection { + + protected: + + Pci::Connection _pci; +}; + +/** + * AHCI driver + */ +class Ahci_driver : public Ahci_pci_connection, + public Ahci_driver_base +{ + public: + + /** + * Constructor + */ + Ahci_driver() : Ahci_driver_base(Ahci_device::probe(_pci)) { } +}; + +#endif /* _AHCI_DRIVER_H_ */ +