ahci: refactor code for suspend/resume

This commit is a preparation commit for suspend/resume. The commit
refactors the code in order to consolidate all Platform resources into one
instance. All users within the driver should access the resources with
with_* functions, which checks whether the device resource is usable. The
callers are not allowed to store any references to the provided resources.

Issue #5101
This commit is contained in:
Alexander Boettcher 2023-12-21 14:01:09 +01:00 committed by Christian Helmuth
parent 8b3a339817
commit cde4d4aee0
5 changed files with 683 additions and 457 deletions

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (C) 2015-2020 Genode Labs GmbH * Copyright (C) 2015-2024 Genode Labs GmbH
* *
* This file is part of the Genode OS framework, which is distributed * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
@ -219,15 +219,15 @@ class Ata::Protocol : public Ahci::Protocol, Noncopyable
** Ahci::Protocol interface ** ** Ahci::Protocol interface **
******************************/ ******************************/
unsigned init(Port &port) override unsigned init(Port &port, Port_mmio &mmio) override
{ {
/* identify device */ /* identify device */
Command_table table(port.command_table_range(0), Command_table table(port.command_table_range(0),
port.device_info_dma_addr, 0x1000); port.device_info_dma_addr, 0x1000);
table.fis.identify_device(); table.fis.identify_device();
port.execute(0); port.execute(0, mmio);
port.wait_for_any(port.delayer, Port::Is::Dss::Equal(1), mmio.wait_for_any(port.delayer, Port::Is::Dss::Equal(1),
Port::Is::Pss::Equal(1), Port::Is::Pss::Equal(1),
Port::Is::Dhrs::Equal(1)); Port::Is::Dhrs::Equal(1));
@ -249,27 +249,26 @@ class Ata::Protocol : public Ahci::Protocol, Noncopyable
cmd_slots = 1; cmd_slots = 1;
_slots.limit((size_t)cmd_slots); _slots.limit((size_t)cmd_slots);
port.ack_irq(); port.ack_irq(mmio);
return cmd_slots; return cmd_slots;
} }
void handle_irq(Port &port) override void handle_irq(Port &port, Port_mmio &mmio) override
{ {
unsigned is = port.read<Port::Is>(); unsigned is = mmio.read<Port::Is>();
/* ncg */ /* ncg */
if (_ncq_support(port) && Port::Is::Fpdma_irq::get(is)) if (_ncq_support(port) && Port::Is::Fpdma_irq::get(is))
do { do {
port.ack_irq(); port.ack_irq(mmio);
} } while (Port::Is::Sdbs::get(mmio.read<Port::Is>()));
while (Port::Is::Sdbs::get(port.read<Port::Is>()));
/* normal dma */ /* normal dma */
else if (Port::Is::Dma_ext_irq::get(port.read<Port::Is>())) else if (Port::Is::Dma_ext_irq::get(mmio.read<Port::Is>()))
port.ack_irq(); port.ack_irq(mmio);
_slot_states = port.read<Port::Ci>() | port.read<Port::Sact>(); _slot_states = mmio.read<Port::Ci>() | mmio.read<Port::Sact>();
port.stop(); port.stop(mmio);
_syncing = false; _syncing = false;
} }
@ -284,7 +283,8 @@ class Ata::Protocol : public Ahci::Protocol, Noncopyable
void writeable(bool rw) override { _writeable = rw; } void writeable(bool rw) override { _writeable = rw; }
Response submit(Port &port, Block::Request const request) override Response submit(Port &port, Block::Request const &request,
Port_mmio &mmio) override
{ {
Block::Operation const op = request.operation; Block::Operation const op = request.operation;
@ -327,9 +327,9 @@ class Ata::Protocol : public Ahci::Protocol, Noncopyable
} else if (_ncq_support(port)) { } else if (_ncq_support(port)) {
table.fis.fpdma(write == false, op.block_number, op.count, slot); table.fis.fpdma(write == false, op.block_number, op.count, slot);
/* ensure that 'Cmd::St' is 1 before writing 'Sact' */ /* ensure that 'Cmd::St' is 1 before writing 'Sact' */
port.start(); port.start(mmio);
/* set pending */ /* set pending */
port.write<Port::Sact>(1U << slot); mmio.write<Port::Sact>(1U << slot);
} else { } else {
table.fis.dma_ext(write == false, op.block_number, op.count); table.fis.dma_ext(write == false, op.block_number, op.count);
} }
@ -339,12 +339,12 @@ class Ata::Protocol : public Ahci::Protocol, Noncopyable
header.write<Command_header::Bits::W>(write ? 1 : 0); header.write<Command_header::Bits::W>(write ? 1 : 0);
header.clear_byte_count(); header.clear_byte_count();
port.execute(slot); port.execute(slot, mmio);
return Response::ACCEPTED; return Response::ACCEPTED;
} }
Block::Request completed(Port & /* port */) override Block::Request completed(Port_mmio &) override
{ {
Block::Request r { }; Block::Request r { };

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (C) 2015-2020 Genode Labs GmbH * Copyright (C) 2015-2024 Genode Labs GmbH
* *
* This file is part of the Genode OS framework, which is distributed * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
@ -18,9 +18,9 @@
#include <util/endian.h> #include <util/endian.h>
namespace Atapi { namespace Atapi {
class Protocol; class Protocol;
using namespace Ahci; using namespace Ahci;
using namespace Genode; using namespace Genode;
} }
class Atapi::Protocol : public Ahci::Protocol, Noncopyable class Atapi::Protocol : public Ahci::Protocol, Noncopyable
@ -31,34 +31,34 @@ class Atapi::Protocol : public Ahci::Protocol, Noncopyable
block_number_t _block_count { 0 }; block_number_t _block_count { 0 };
size_t _block_size { 2048 }; size_t _block_size { 2048 };
void _atapi_command(Port &port) void _atapi_command(Port &port, Port_mmio &mmio)
{ {
Command_header header(port.command_header_range(0)); Command_header header(port.command_header_range(0));
header.atapi_command(); header.atapi_command();
header.clear_byte_count(); header.clear_byte_count();
port.execute(0); port.execute(0, mmio);
} }
void _read_sense(Port &port) void _read_sense(Port &port, Port_mmio &mmio)
{ {
Command_table table(port.command_table_range(0), Command_table table(port.command_table_range(0),
port.device_info_dma_addr, 0x1000); port.device_info_dma_addr, 0x1000);
table.fis.atapi(); table.fis.atapi();
table.atapi_cmd.read_sense(); table.atapi_cmd.read_sense();
_atapi_command(port); _atapi_command(port, mmio);
} }
void _test_unit_ready(Port &port) void _test_unit_ready(Port &port, Port_mmio &mmio)
{ {
Command_table table(port.command_table_range(0), 0, 0); Command_table table(port.command_table_range(0), 0, 0);
table.fis.atapi(); table.fis.atapi();
table.atapi_cmd.test_unit_ready(); table.atapi_cmd.test_unit_ready();
_atapi_command(port); _atapi_command(port, mmio);
} }
void _read_capacity(Port &port) void _read_capacity(Port &port, Port_mmio &mmio)
{ {
Command_table table(port.command_table_range(0), Command_table table(port.command_table_range(0),
port.device_info_dma_addr, 0x1000); port.device_info_dma_addr, 0x1000);
@ -67,16 +67,16 @@ class Atapi::Protocol : public Ahci::Protocol, Noncopyable
table.atapi_cmd.read_capacity(); table.atapi_cmd.read_capacity();
_atapi_command(port); _atapi_command(port, mmio);
} }
void _start_unit(Port &port) void _start_unit(Port &port, Port_mmio &mmio)
{ {
Command_table table(port.command_table_range(0), 0, 0); Command_table table(port.command_table_range(0), 0, 0);
table.fis.atapi(); table.fis.atapi();
table.atapi_cmd.start_unit(); table.atapi_cmd.start_unit();
_atapi_command(port); _atapi_command(port, mmio);
} }
public: public:
@ -85,41 +85,41 @@ class Atapi::Protocol : public Ahci::Protocol, Noncopyable
** Ahci::Protocol interface ** ** Ahci::Protocol interface **
******************************/ ******************************/
unsigned init(Port &port) override unsigned init(Port &port, Port_mmio &mmio) override
{ {
port.write<Port::Cmd::Atapi>(1); mmio.write<Port::Cmd::Atapi>(1);
retry<Port::Polling_timeout>( retry<Port::Polling_timeout>(
[&] { [&] {
_start_unit(port); _start_unit(port, mmio);
port.wait_for_any(port.delayer, mmio.wait_for_any(port.delayer,
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1), Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
Port::Is::Dhrs::Equal(1)); Port::Is::Dhrs::Equal(1));
port.ack_irq(); port.ack_irq(mmio);
/* read sense */ /* read sense */
_read_sense(port); _read_sense(port, mmio);
port.wait_for_any(port.delayer, mmio.wait_for_any(port.delayer,
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1), Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
Port::Is::Dhrs::Equal(1)); Port::Is::Dhrs::Equal(1));
port.ack_irq(); port.ack_irq(mmio);
/* test unit ready */ /* test unit ready */
_test_unit_ready(port); _test_unit_ready(port, mmio);
port.wait_for(port.delayer, Port::Is::Dhrs::Equal(1)); mmio.wait_for(port.delayer, Port::Is::Dhrs::Equal(1));
port.ack_irq(); port.ack_irq(mmio);
Device_fis f(*port.fis); Device_fis f(*port.fis);
/* check if devic is ready */ /* check if devic is ready */
if (!f.read<Device_fis::Status::Device_ready>() || f.read<Device_fis::Error>()) if (!f.read<Device_fis::Status::Device_ready>() || f.read<Device_fis::Error>())
throw Port::Polling_timeout(); throw Port::Polling_timeout();
_read_capacity(port); _read_capacity(port, mmio);
port.wait_for_any(port.delayer, mmio.wait_for_any(port.delayer,
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1), Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
Port::Is::Dhrs::Equal(1)); Port::Is::Dhrs::Equal(1));
port.ack_irq(); port.ack_irq(mmio);
_block_count = host_to_big_endian(((unsigned *)port.device_info->start)[0]) + 1; _block_count = host_to_big_endian(((unsigned *)port.device_info->start)[0]) + 1;
_block_size = host_to_big_endian(((unsigned *)port.device_info->start)[1]); _block_size = host_to_big_endian(((unsigned *)port.device_info->start)[1]);
@ -137,14 +137,15 @@ class Atapi::Protocol : public Ahci::Protocol, Noncopyable
.writeable = false }; .writeable = false };
} }
void handle_irq(Port &port) override void handle_irq(Port &port, Port_mmio &mmio) override
{ {
port.ack_irq(); port.ack_irq(mmio);
} }
void writeable(bool) override { } void writeable(bool) override { }
Response submit(Port &port, Block::Request const request) override Response submit(Port &port, Block::Request const &request,
Port_mmio &mmio) override
{ {
if (request.operation.type != Block::Operation::Type::READ || if (request.operation.type != Block::Operation::Type::READ ||
port.sanity_check(request) == false || port.dma_base == 0) port.sanity_check(request) == false || port.dma_base == 0)
@ -172,15 +173,14 @@ class Atapi::Protocol : public Ahci::Protocol, Noncopyable
header.write<Command_header::Bits::W>(0); header.write<Command_header::Bits::W>(0);
header.clear_byte_count(); header.clear_byte_count();
/* set pending */ port.execute(0, mmio);
port.execute(0);
return Response::ACCEPTED; return Response::ACCEPTED;
} }
Block::Request completed(Port &port) override Block::Request completed(Port_mmio &mmio) override
{ {
if (!_pending.operation.valid() || port.read<Port::Ci>()) if (!_pending.operation.valid() || mmio.read<Port::Ci>())
return Block::Request(); return Block::Request();
Block::Request request = _pending; Block::Request request = _pending;

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (C) 2016-2020 Genode Labs GmbH * Copyright (C) 2016-2024 Genode Labs GmbH
* *
* This file is part of the Genode OS framework, which is distributed * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
@ -49,30 +49,26 @@ class Ahci::Driver : Noncopyable
private: private:
Env &_env;
Dispatch &_dispatch;
struct Timer_delayer : Mmio<0>::Delayer, Timer::Connection struct Timer_delayer : Mmio<0>::Delayer, Timer::Connection
{ {
using Timer::Connection::Connection; using Timer::Connection::Connection;
void usleep(uint64_t us) override { Timer::Connection::usleep(us); } void usleep(uint64_t us) override { Timer::Connection::usleep(us); }
} _delayer { _env }; };
Signal_handler<Driver> _handler { _env.ep(), *this, &Driver::handle_irq }; Env & _env;
Dispatch & _dispatch;
Timer_delayer _delayer { _env };
Signal_handler<Driver> _handler { _env.ep(), *this, &Driver::handle_irq };
Resources _resources { _env, _handler };
Platform::Connection _plat { _env }; Constructible<Ata::Protocol> _ata [MAX_PORTS];
Platform::Device _device { _plat };
Hba _hba { _device, _handler, _plat };
Constructible<Ata::Protocol> _ata[MAX_PORTS];
Constructible<Atapi::Protocol> _atapi[MAX_PORTS]; Constructible<Atapi::Protocol> _atapi[MAX_PORTS];
Constructible<Port> _ports[MAX_PORTS]; Constructible<Port> _ports[MAX_PORTS];
bool _enable_atapi; bool _enable_atapi;
unsigned _scan_ports(Region_map &rm) unsigned _scan_ports(Region_map &rm, Platform::Connection &plat, Hba &hba)
{ {
log("port scan:"); log("port scan:");
@ -80,17 +76,17 @@ class Ahci::Driver : Noncopyable
for (unsigned index = 0; index < MAX_PORTS; index++) { for (unsigned index = 0; index < MAX_PORTS; index++) {
if (_hba.port_implemented(index) == false) Port_base port(index, plat, hba, _delayer);
continue;
Port_base port(index, _plat, _hba, _delayer); if (port.implemented() == false)
continue;
bool enabled = false; bool enabled = false;
if (port.ata()) { if (port.ata()) {
try { try {
_ata[index].construct(); _ata[index].construct();
_ports[index].construct(*_ata[index], rm, _plat, _ports[index].construct(*_ata[index], rm, plat,
_hba, _delayer, index); hba, _delayer, index);
enabled = true; enabled = true;
} catch (...) { } } catch (...) { }
@ -98,8 +94,8 @@ class Ahci::Driver : Noncopyable
} else if (port.atapi() && _enable_atapi) { } else if (port.atapi() && _enable_atapi) {
try { try {
_atapi[index].construct(); _atapi[index].construct();
_ports[index].construct(*_atapi[index], rm, _plat, _ports[index].construct(*_atapi[index], rm, plat,
_hba, _delayer, index); hba, _delayer, index);
enabled = true; enabled = true;
} catch (...) { } } catch (...) { }
@ -121,11 +117,15 @@ class Ahci::Driver : Noncopyable
: _env(env), _dispatch(dispatch), _enable_atapi(support_atapi) : _env(env), _dispatch(dispatch), _enable_atapi(support_atapi)
{ {
/* search for devices */ /* search for devices */
unsigned port_count = _scan_ports(env.rm()); _resources.with_platform([&](auto &platform) {
_resources.with_hba([&](auto &hba) {
unsigned port_count = _scan_ports(env.rm(), platform, hba);
if (port_count != _hba.port_count()) if (port_count != hba.port_count())
log("controller port count differs from detected ports (CAP.NP=", log("controller port count differs from detected ports (CAP.NP=",
Hex(_hba.cap_np_value()), ",PI=", Hex(_hba.pi_value()), ")"); Hex(hba.cap_np_value()), ",PI=", Hex(hba.pi_value()), ")");
});
});
} }
/** /**
@ -133,12 +133,14 @@ class Ahci::Driver : Noncopyable
*/ */
void handle_irq() void handle_irq()
{ {
_hba.handle_irq([&] (unsigned port) { _resources.with_hba([&](auto &hba) {
if (_ports[port].constructed()) hba.handle_irq([&] (unsigned port) {
_ports[port]->handle_irq(); if (_ports[port].constructed())
_ports[port]->handle_irq();
/* handle (pending) requests */ /* handle (pending) requests */
_dispatch.session(port); _dispatch.session(port);
}, [&]() { error("hba handle_irq failed"); });
}); });
} }
@ -295,19 +297,15 @@ struct Ahci::Block_session_component : Rpc_object<Block::Session>,
}; };
struct Ahci::Main : Rpc_object<Typed_root<Block::Session>>, struct Ahci::Main : Rpc_object<Typed_root<Block::Session>>, Dispatch
Dispatch
{ {
Env &env; Env & env;
Attached_rom_dataspace config { env, "config" };
Attached_rom_dataspace config { env, "config" }; Constructible<Ahci::Driver> driver { };
Constructible<Reporter> reporter { };
Constructible<Ahci::Driver> driver { };
Constructible<Reporter> reporter { };
Constructible<Block_session_component> block_session[Driver::MAX_PORTS]; Constructible<Block_session_component> block_session[Driver::MAX_PORTS];
Main(Env &env) Main(Env &env) : env(env)
: env(env)
{ {
log("--- Starting AHCI driver ---"); log("--- Starting AHCI driver ---");
bool support_atapi = config.xml().attribute_value("atapi", false); bool support_atapi = config.xml().attribute_value("atapi", false);

View File

@ -1,11 +1,11 @@
/* /*
* \brief Utilitize used by the AHCI driver * \brief Utilities used by the AHCI driver
* \author Josef Soentgen * \author Josef Soentgen
* \date 2018-03-05 * \date 2018-03-05
*/ */
/* /*
* Copyright (C) 2018 Genode Labs GmbH * Copyright (C) 2018-2024 Genode Labs GmbH
* *
* This file is part of the Genode OS framework, which is distributed * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.