genode/repos/os/src/drivers/ahci/ahci.cc

279 lines
6.1 KiB
C++

/**
* \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 <timer_session/connection.h>
/* local includes */
#include <ata_driver.h>
#include <atapi_driver.h>
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<Ahci> 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<Hba::Is>();
while (port_list) {
unsigned port = log2(port_list);
port_list &= ~(1U << port);
ports[port]->handle_irq();
}
/* clear status register */
hba.ack_irq();
/* ack at interrupt controller */
platform_hba.ack_irq();
}
/*
* 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<Hba::Version::Major>()), " "
"minor=", Genode::Hex(hba.read<Hba::Version::Minor>()));
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<Hba::Pi>()));
unsigned available = hba.read<Hba::Pi>();
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<Port_base::Sig>()) {
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<Ata_driver *>(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<Ata_driver *>(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());
}
});
}
});
}