diff --git a/repos/os/src/drivers/usb_block/main.cc b/repos/os/src/drivers/usb_block/main.cc index cbccece76..eb068f5d3 100644 --- a/repos/os/src/drivers/usb_block/main.cc +++ b/repos/os/src/drivers/usb_block/main.cc @@ -1,11 +1,12 @@ /* * \brief Usb session to Block session translator * \author Josef Soentgen + * \author Sebastian Sumpf * \date 2016-02-08 */ /* - * 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. @@ -13,14 +14,13 @@ /* Genode includes */ #include +#include #include #include #include #include #include -#include -#include -#include +#include #include #include #include @@ -34,8 +34,11 @@ static bool verbose_scsi = false; namespace Usb { using namespace Genode; + using namespace Block; + using Response = Block::Request_stream::Response; struct Block_driver; + struct Block_session_component; struct Main; } @@ -44,8 +47,7 @@ namespace Usb { ** USB Mass Storage (BBB) Block::Driver implementation ** *********************************************************/ -struct Usb::Block_driver : Usb::Completion, - Block::Driver +struct Usb::Block_driver : Usb::Completion { Env &env; Entrypoint &ep; @@ -57,13 +59,34 @@ struct Usb::Block_driver : Usb::Completion, */ struct Block_request { - Block::Packet_descriptor packet { }; - Block::sector_t lba { 0 }; - char *buffer { nullptr }; - size_t size { 0 }; - bool read { false }; - bool pending { false }; - } req { }; + Request block_request; + addr_t address { 0 }; + size_t size { 0 }; + bool completed { false }; + + Block_request(Request &request, Request_stream::Payload const &payload) + : block_request(request) + { + payload.with_content(request, [&](void *addr, size_t sz) { + address = addr_t(addr); + size = sz; + }); + } + + bool read() const + { + return block_request.operation.type == Operation::Type::READ; + } + + bool write() const + { + return block_request.operation.type == Operation::Type::WRITE; + } + }; + + Constructible request { }; + + bool request_pending() const { return request.constructed(); } bool initialized = false; bool device_plugged = false; @@ -74,21 +97,21 @@ struct Usb::Block_driver : Usb::Completion, void handle_state_change() { if (!usb.plugged()) { - Genode::log("Device unplugged"); + log("Device unplugged"); device_plugged = false; return; } if (initialized) { - Genode::error("Device was already initialized"); + error("Device was already initialized"); return; } - Genode::log("Device plugged"); + log("Device plugged"); if (!initialize()) { env.parent().exit(-1); - Genode::sleep_forever(); + sleep_forever(); return; } @@ -107,23 +130,26 @@ struct Usb::Block_driver : Usb::Completion, /* * Read Usb session label from the configuration */ - static char const *get_label(Xml_node node) + template + static Genode::String get_label(Xml_node node) { - static Genode::String<256> usb_label; + Genode::String usb_label { "usb_storage" }; try { node.attribute("label").value(usb_label); return usb_label.string(); } catch (...) { } - return "usb_storage"; + return usb_label; } /* * USB session */ - Allocator_avl alloc; - Usb::Connection usb { env, &alloc, get_label(config.xml()), 2 * (1<<20), state_change_dispatcher }; - Usb::Device device; + Allocator_avl alloc; + Usb::Connection usb { env, &alloc, + get_label<128>(config.xml()).string(), 2 * (1<<20), state_change_dispatcher }; + Usb::Device device; + Signal_handler
&wake_up_handler; /* * Reporter @@ -188,13 +214,13 @@ struct Usb::Block_driver : Usb::Completion, Usb::Interface iface = device.interface(interface); if (p.type != Packet_descriptor::BULK) { - Genode::error("Can only handle BULK packets"); + error("Can only handle BULK packets"); iface.release(p); return; } if (!p.succeded) { - Genode::error("init complete error: packet not succeeded"); + error("init complete error: packet not succeeded"); iface.release(p); return; } @@ -217,7 +243,7 @@ struct Usb::Block_driver : Usb::Completion, if (verbose_scsi) r.dump(); if (!r.sbc()) { - Genode::warning("Device does not use SCSI Block Commands and may not work"); + warning("Device does not use SCSI Block Commands and may not work"); } r.get_id(vendor, sizeof(vendor)); @@ -265,7 +291,7 @@ struct Usb::Block_driver : Usb::Completion, case NOT_READY_TO_READY_CHANGE: /* asq == 0x00 */ case POWER_ON_OR_RESET_OCCURRED: /* asq == 0x00 */ - Genode::warning("Not ready - try again"); + warning("Not ready - try again"); try_again = true; break; @@ -294,7 +320,7 @@ struct Usb::Block_driver : Usb::Completion, uint32_t const sig = csw.sig(); if (sig != Csw::SIG) { - Genode::error("CSW signature does not match: ", + error("CSW signature does not match: ", Hex(sig, Hex::PREFIX, Hex::PAD)); break; } @@ -302,7 +328,7 @@ struct Usb::Block_driver : Usb::Completion, uint32_t const tag = csw.tag(); uint8_t const status = csw.sts(); if (status != Csw::PASSED) { - Genode::error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD), + error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD), " tag: ", tag); break; } @@ -363,7 +389,7 @@ struct Usb::Block_driver : Usb::Completion, Block::sector_t count, size_t size) { try { - Genode::Reporter::Xml_generator xml(reporter, [&] () { + Reporter::Xml_generator xml(reporter, [&] () { xml.node("device", [&] () { xml.attribute("vendor", vendor); xml.attribute("product", product); @@ -372,7 +398,7 @@ struct Usb::Block_driver : Usb::Completion, xml.attribute("writeable", _writeable); }); }); - } catch (...) { Genode::warning("Could not report block device"); } + } catch (...) { warning("Could not report block device"); } } /** @@ -392,10 +418,10 @@ struct Usb::Block_driver : Usb::Completion, Usb::Interface &iface = device.interface(active_interface); try { iface.claim(); } catch (Usb::Session::Interface_already_claimed) { - Genode::error("Device already claimed"); + error("Device already claimed"); return false; } catch (Usb::Session::Interface_not_found) { - Genode::error("Interface not found"); + error("Interface not found"); return false; } @@ -410,7 +436,7 @@ struct Usb::Block_driver : Usb::Completion, if (alt_iface.iclass != ICLASS_MASS_STORAGE || alt_iface.isubclass != ISUBCLASS_SCSI || alt_iface.iprotocol != IPROTO_BULK_ONLY) { - Genode::error("No mass storage SCSI bulk-only device"); + error("No mass storage SCSI bulk-only device"); return false; } @@ -437,7 +463,7 @@ struct Usb::Block_driver : Usb::Completion, Usb::Packet_descriptor p = iface.alloc(0); iface.control_transfer(p, 0x21, 0xff, 0, active_interface, 100); if (!p.succeded) { - Genode::error("Could not reset device"); + error("Could not reset device"); iface.release(p); throw -1; } @@ -475,7 +501,7 @@ struct Usb::Block_driver : Usb::Completion, csw(init, true); if (!init.inquiry) { - Genode::warning("Inquiry_cmd failed"); + warning("Inquiry_cmd failed"); throw -1; } @@ -502,7 +528,7 @@ struct Usb::Block_driver : Usb::Completion, resp(Scsi::Request_sense_response::LENGTH, init, true); csw(init, true); if (!init.request_sense) { - Genode::warning("Request_sense failed"); + warning("Request_sense failed"); throw -1; } @@ -524,7 +550,7 @@ struct Usb::Block_driver : Usb::Completion, timer.msleep(1000); } if (retries == MAX_RETRIES) { - Genode::warning("Test_unit_ready_cmd failed"); + warning("Test_unit_ready_cmd failed"); throw -1; } } @@ -547,7 +573,7 @@ struct Usb::Block_driver : Usb::Completion, csw(init, true); if (!init.read_capacity) { - Genode::warning("Read_capacity_cmd failed"); + warning("Read_capacity_cmd failed"); throw -1; } @@ -563,7 +589,7 @@ struct Usb::Block_driver : Usb::Completion, csw(init, true); if (!init.read_capacity) { - Genode::warning("Read_capacity_cmd failed"); + warning("Read_capacity_cmd failed"); throw -1; } @@ -582,7 +608,7 @@ struct Usb::Block_driver : Usb::Completion, device.manufactorer_string.to_char(vendor, sizeof(vendor)); device.product_string.to_char(product, sizeof(product)); - Genode::log("Found USB device: ", (char const*)vendor, " (", + log("Found USB device: ", (char const*)vendor, " (", (char const*)product, ") block size: ", _block_size, " count: ", _block_count); @@ -592,10 +618,10 @@ struct Usb::Block_driver : Usb::Completion, return true; } catch (int) { /* handle command failures */ - Genode::error("Could not initialize storage device"); + error("Could not initialize storage device"); } catch (...) { /* handle Usb::Session failures */ - Genode::error("Could not initialize storage device"); + error("Could not initialize storage device"); } return false; } @@ -609,31 +635,23 @@ struct Usb::Block_driver : Usb::Completion, bool execute_pending_request() { Usb::Interface &iface = device.interface(active_interface); - Usb::Endpoint ep = iface.endpoint(req.read ? ep_in : ep_out); - Usb::Packet_descriptor p = iface.alloc(req.size); + Usb::Endpoint ep = iface.endpoint(request->read() ? ep_in : ep_out); + Usb::Packet_descriptor p = iface.alloc(request->size); - if (!req.read) memcpy(iface.content(p), req.buffer, req.size); + if (request->write()) + memcpy(iface.content(p), (void *)request->address, request->size); iface.bulk_transfer(p, ep, false, this); return true; } - /** - * Acknowledge currently pending request - * - * After receiving the CSW ack the request at the Block session. - */ - void ack_pending_request(bool success = true) + void wakeup_client(bool success) { - /* - * Needs to be reset bevor calling ack_packet to prevent getting a new - * request imediately and throwing Request_congestion() in io() again. - */ - req.pending = false; + request->block_request.success = success; + request->completed = true; - Block::Packet_descriptor p = req.packet; - ack_packet(p, success); + wake_up_handler.dispatch(0); } /** @@ -648,18 +666,19 @@ struct Usb::Block_driver : Usb::Completion, Usb::Interface iface = device.interface(active_interface); if (p.type != Packet_descriptor::BULK) { - Genode::error("No BULK packet"); + error("No BULK packet"); iface.release(p); return; } if (!p.succeded) { - Genode::error("complete error: packet not succeded"); - if (req.pending) { - Genode::error("request pending: tag: ", active_tag, " read: ", - (int)req.read, " buffer: ", (void *)req.buffer, " lba: ", - req.lba, " size: ", req.size); - ack_pending_request(false); + error("complete error: packet not succeded"); + if (request_pending()) { + error("request pending: tag: ", active_tag, " read: ", + request->read(), " buffer: ", Hex(request->address), + " lba: ", request->block_request.operation.block_number, + " size: ", request->size); + wakeup_client(false); } iface.release(p); return; @@ -668,7 +687,7 @@ struct Usb::Block_driver : Usb::Completion, static bool request_executed = false; if (!p.read_transfer()) { /* send read/write request */ - if (req.pending) { + if (request_pending()) { /* * The CBW was successfully sent to the device, now read/write the @@ -688,16 +707,16 @@ struct Usb::Block_driver : Usb::Completion, int actual_size = p.transfer.actual_size; if (actual_size < 0) { - Genode::error("Transfer actual size: ", actual_size); + error("Transfer actual size: ", actual_size); actual_size = 0; } /* the size indicates an IN I/O packet */ if ((uint32_t)actual_size >= _block_size) { - if (req.pending) { + if (request_pending()) { /* the content was successfully read, get the CSW */ - memcpy(req.buffer, iface.content(p), actual_size); + memcpy((void *)request->address, iface.content(p), actual_size); csw(*this); } @@ -707,41 +726,42 @@ struct Usb::Block_driver : Usb::Completion, /* when ending up here, we should have gotten an CSW packet */ if (actual_size != Csw::LENGTH) - Genode::warning("This is not the actual size you are looking for"); + warning("This is not the actual size you are looking for"); do { Csw csw((addr_t)iface.content(p)); uint32_t const sig = csw.sig(); if (sig != Csw::SIG) { - Genode::error("CSW signature does not match: ", - Hex(sig, Hex::PREFIX, Hex::PAD)); + error("CSW signature does not match: ", + Hex(sig, Hex::PREFIX, Hex::PAD)); break; } uint32_t const tag = csw.tag(); if (tag != active_tag) { - Genode::error("CSW tag mismatch. Got ", tag, " expected: ", - active_tag); + error("CSW tag mismatch. Got ", tag, " expected: ", + active_tag); break; } uint8_t const status = csw.sts(); if (status != Csw::PASSED) { - Genode::error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD), - " read: ", (int)req.read, " buffer: ", (void *)req.buffer, - " lba: ", req.lba, " size: ", req.size); + error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD), + " read: ", request->read(), " buffer: ", (void *)request->address, + " lba: ", request->block_request.operation.block_number, + " size: ", request->size); break; } uint32_t const dr = csw.dr(); if (dr) { - Genode::warning("CSW data residue: ", dr, " not considered"); + warning("CSW data residue: ", dr, " not considered"); } /* ack Block::Packet_descriptor */ request_executed = false; - ack_pending_request(); + wakeup_client(true); } while (0); iface.release(p); @@ -767,12 +787,12 @@ struct Usb::Block_driver : Usb::Completion, * \param ep Server::Endpoint * \param sigh signal context used for annoucing Block service */ - Block_driver(Env &env, Genode::Allocator &alloc, - Genode::Signal_context_capability sigh) + Block_driver(Env &env, Allocator &alloc, + Genode::Signal_context_capability sigh, + Signal_handler
&wake_up_handler) : - Block::Driver(env.ram()), env(env), ep(env.ep()), announce_sigh(sigh), alloc(&alloc), - device(&alloc, usb, ep) + device(&alloc, usb, ep), wake_up_handler(wake_up_handler) { parse_config(config.xml()); reporter.enabled(true); @@ -789,111 +809,196 @@ struct Usb::Block_driver : Usb::Completion, /** * Send CBW */ - void send_cbw(Block::sector_t lba, size_t len, bool read) + void send_cbw(block_number_t lba, block_count_t count, bool read) { uint32_t const t = new_tag(); char cb[Cbw::LENGTH]; if (read) { - if (force_cmd_16) Read_16 r((addr_t)cb, t, active_lun, lba, len, _block_size); - else Read_10 r((addr_t)cb, t, active_lun, lba, len, _block_size); + if (force_cmd_16) Read_16 r((addr_t)cb, t, active_lun, lba, count, _block_size); + else Read_10 r((addr_t)cb, t, active_lun, lba, count, _block_size); } else { - if (force_cmd_16) Write_16 w((addr_t)cb, t, active_lun, lba, len, _block_size); - else Write_10 w((addr_t)cb, t, active_lun, lba, len, _block_size); + if (force_cmd_16) Write_16 w((addr_t)cb, t, active_lun, lba, count, _block_size); + else Write_10 w((addr_t)cb, t, active_lun, lba, count, _block_size); } cbw(cb, *this); } - /** - * Perform IO/ request - * - * \param read set to true when reading, false when writting - * \param lba address of the starting block - * \param buffer source/destination buffer - * \param p Block::Packet_descriptor - */ - void io(bool read, Block::sector_t lba, size_t count, - char *buffer, Block::Packet_descriptor &p) - { - if (!device_plugged) throw Io_error(); - if (lba+count > _block_count) throw Io_error(); - if (req.pending) throw Request_congestion(); + /******************************************* + ** interface to Block_session_component ** + *******************************************/ - req.pending = true; - req.packet = p; - req.lba = lba; - req.size = count * _block_size; - req.buffer = buffer; - req.read = read; - - send_cbw(lba, count, read); - } - - /******************************* - ** Block::Driver interface ** - *******************************/ - - Block::Session::Info info() const override + Block::Session::Info info() const { return { .block_size = _block_size, .block_count = _block_count, - .align_log2 = Genode::log2(_block_size), + .align_log2 = log2(_block_size), .writeable = _writeable }; } - void read(Block::sector_t lba, size_t count, - char *buffer, Block::Packet_descriptor &p) override { - io(true, lba, count, buffer, p); } + Response submit(Request &block_request, + Request_stream::Payload const &payload) + { + if (device_plugged == false) + return Response::REJECTED; - void write(Block::sector_t lba, size_t count, - char const *buffer, Block::Packet_descriptor &p) override { - io(false, lba, count, const_cast(buffer), p); } + Operation const &op = block_request.operation; - void sync() override { /* maybe implement SYNCHRONIZE_CACHE_10/16? */ } + /* read only */ + if (info().writeable == false && + op.type == Operation::Type::WRITE) + return Response::REJECTED; + + /* range check */ + block_number_t const last = op.block_number + op.count; + if (last >= info().block_count) + return Response::REJECTED; + + /* check if request is pending */ + if (request_pending()) + return Response::RETRY; + + request.construct(block_request, payload); + + /* execute */ + send_cbw(op.block_number, op.count, request->read()); + + return Response::ACCEPTED; + } + + template + void with_completed(FUNC const &fn) + { + if (request_pending() == false || request->completed == false) return; + + fn(request->block_request); + + request.destruct(); + } }; -struct Usb::Main +struct Usb::Block_session_component : Rpc_object, + Block::Request_stream +{ + Env &_env; + + Block_session_component(Env &env, Dataspace_capability ds, + Signal_context_capability sigh, + Block::Session::Info info) + : + Request_stream(env.rm(), ds, env.ep(), sigh, info), + _env(env) + { + _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(); } +}; + + + +struct Usb::Main : Rpc_object> { Env &env; Heap heap { env.ram(), env.rm() }; - void announce() - { - env.parent().announce(env.ep().manage(root)); - } + Constructible block_ds { }; + Constructible block_session { }; Signal_handler
announce_dispatcher { env.ep(), *this, &Usb::Main::announce }; - struct Factory : Block::Driver_factory + Io_signal_handler
request_handler { + env.ep(), *this, &Main::handle_requests }; + + Block_driver driver { env, heap, announce_dispatcher, request_handler }; + + void announce() { - Env &env; - Allocator &alloc; - Signal_context_capability sigh; - Usb::Block_driver driver; + env.parent().announce(env.ep().manage(*this)); + } - Factory(Env &env, Allocator &alloc, - Signal_context_capability sigh) - : env(env), alloc(alloc), sigh(sigh), - driver(env, alloc, sigh) { } + /** + * There can only be one request in flight for this driver, so keep it simple + */ + void handle_requests() + { + if (!block_session.constructed()) return; - Block::Driver *create() override { return &driver; } + /* ack and release possibly pending packet */ + block_session->try_acknowledge([&] (Block_session_component::Ack &ack) { + driver.with_completed([&] (Block::Request &request) { + ack.submit(request); + }); + }); - void destroy(Block::Driver *) override { } + block_session->with_requests([&] (Request request) { - private: + /* only read/write for now */ + if (Operation::has_payload(request.operation.type) == false) { + request.success = true; + return Response::REJECTED; + } - /* - * Noncopyable - */ - Factory(Factory const &); - Factory &operator = (Factory const &); - }; + Response response = Response::RETRY; - Factory factory { env, heap, announce_dispatcher }; - Block::Root root { env.ep(), heap, env.rm(), factory, true }; + block_session->with_payload([&] (Request_stream::Payload const &payload) { + response = driver.submit(request, payload); + }); + return response; + }); + + block_session->wakeup_client_if_needed(); + } + + + /***************************** + ** Block session interface ** + *****************************/ + + Genode::Session_capability session(Root::Session_args const &args, + Affinity const &) override + { + log("new block session: ", args.string()); + + if (block_session.constructed()) { + error("device is already in use"); + throw Service_denied(); + } + + size_t const ds_size = + Arg_string::find_arg(args.string(), "tx_buf_size").ulong_value(0); + + Ram_quota const ram_quota = ram_quota_from_args(args.string()); + + if (ds_size >= ram_quota.value) { + warning("communication buffer size exceeds session quota"); + throw Insufficient_ram_quota(); + } + + block_ds.construct(env.ram(), env.rm(), ds_size); + block_session.construct(env, block_ds->cap(), request_handler, + driver.info()); + + return block_session->cap(); + } + + void upgrade(Genode::Session_capability, Root::Upgrade_args const&) override { } + + void close(Genode::Session_capability cap) override + { + if (!block_session.constructed() || !(block_session->cap() == cap)) + return; + + block_session.destruct(); + block_ds.destruct(); + } Main(Env &env) : env(env) { } };