diff --git a/repos/base-linux/src/core/include/region_map_component.h b/repos/base-linux/src/core/include/region_map_component.h index 9b9c318d82..aa0bddd726 100644 --- a/repos/base-linux/src/core/include/region_map_component.h +++ b/repos/base-linux/src/core/include/region_map_component.h @@ -66,6 +66,11 @@ class Genode::Region_map_component : public Rpc_object<Region_map>, Rm_dataspace_component *dataspace_component() { return nullptr; } void address_space(Platform_pd *) { } + + using Attach_dma_result = Pd_session::Attach_dma_result; + + Attach_dma_result attach_dma(Dataspace_capability, addr_t) { + return Pd_session::Attach_dma_error::DENIED; }; }; diff --git a/repos/base/include/pd_session/client.h b/repos/base/include/pd_session/client.h index 70239720ab..e578f192cd 100644 --- a/repos/base/include/pd_session/client.h +++ b/repos/base/include/pd_session/client.h @@ -95,6 +95,11 @@ struct Genode::Pd_session_client : Rpc_client<Pd_session> Managing_system_state managing_system(Managing_system_state const & state) override { return call<Rpc_managing_system>(state); } + + addr_t dma_addr(Ram_dataspace_capability ds) override { return call<Rpc_dma_addr>(ds); } + + Attach_dma_result attach_dma(Dataspace_capability ds, addr_t at) override { + return call<Rpc_attach_dma>(ds, at); } }; #endif /* _INCLUDE__PD_SESSION__CLIENT_H_ */ diff --git a/repos/base/include/pd_session/connection.h b/repos/base/include/pd_session/connection.h index eab34bee92..49a8911a4a 100644 --- a/repos/base/include/pd_session/connection.h +++ b/repos/base/include/pd_session/connection.h @@ -37,6 +37,21 @@ struct Genode::Pd_connection : Connection<Pd_session>, Pd_session_client RAM_QUOTA, CAP_QUOTA, label, space)), Pd_session_client(cap()) { } + + struct Device_pd { }; + + /** + * Constructor used for creating device protection domains + */ + Pd_connection(Env &env, Device_pd) + : + Connection<Pd_session>(env, session(env.parent(), + "ram_quota=%u, cap_quota=%u, " + "label=\"device PD\", virt_space=%u, " + "managing_system=yes", + RAM_QUOTA, CAP_QUOTA, UNCONSTRAIN)), + Pd_session_client(cap()) + { } }; #endif /* _INCLUDE__PD_SESSION__CONNECTION_H_ */ diff --git a/repos/base/include/pd_session/pd_session.h b/repos/base/include/pd_session/pd_session.h index dfc81365b8..9f6c7871c4 100644 --- a/repos/base/include/pd_session/pd_session.h +++ b/repos/base/include/pd_session/pd_session.h @@ -312,6 +312,37 @@ struct Genode::Pd_session : Session, Ram_allocator virtual Managing_system_state managing_system(Managing_system_state const &) = 0; + /******************************************* + ** Support for user-level device drivers ** + *******************************************/ + + /** + * Return start address of the dataspace to be used for DMA transfers + * + * The intended use of this function is the use of RAM dataspaces as DMA + * buffers. On systems without IOMMU, device drivers need to know the + * physical address of DMA buffers for issuing DMA transfers. + * + * \return DMA address, or 0 if the dataspace is invalid or the + * PD lacks the permission to obtain the information + */ + virtual addr_t dma_addr(Ram_dataspace_capability) = 0; + + enum class Attach_dma_error { OUT_OF_RAM, OUT_OF_CAPS, DENIED }; + struct Attach_dma_ok { }; + + using Attach_dma_result = Attempt<Attach_dma_ok, Attach_dma_error>; + + /** + * Attach dataspace to I/O page table at specified address 'at' + * + * This operation is preserved to privileged system-management components + * like the platform driver to assign DMA buffers to device protection + * domains. The attach can be reverted by using 'address_space().detach()'. + */ + virtual Attach_dma_result attach_dma(Dataspace_capability, addr_t at) = 0; + + /********************* ** RPC declaration ** *********************/ @@ -361,6 +392,9 @@ struct Genode::Pd_session : Session, Ram_allocator GENODE_RPC(Rpc_managing_system, Managing_system_state, managing_system, Managing_system_state const &); + GENODE_RPC(Rpc_dma_addr, addr_t, dma_addr, Ram_dataspace_capability); + GENODE_RPC(Rpc_attach_dma, Attach_dma_result, attach_dma, + Dataspace_capability, addr_t); GENODE_RPC_INTERFACE(Rpc_assign_parent, Rpc_assign_pci, Rpc_map, Rpc_alloc_signal_source, Rpc_free_signal_source, @@ -370,7 +404,8 @@ struct Genode::Pd_session : Session, Ram_allocator Rpc_transfer_cap_quota, Rpc_cap_quota, Rpc_used_caps, Rpc_try_alloc, Rpc_free, Rpc_transfer_ram_quota, Rpc_ram_quota, Rpc_used_ram, - Rpc_native_pd, Rpc_managing_system); + Rpc_native_pd, Rpc_managing_system, + Rpc_dma_addr, Rpc_attach_dma); }; #endif /* _INCLUDE__PD_SESSION__PD_SESSION_H_ */ diff --git a/repos/base/src/core/include/pd_session_component.h b/repos/base/src/core/include/pd_session_component.h index d18991e26e..1b84ba015a 100644 --- a/repos/base/src/core/include/pd_session_component.h +++ b/repos/base/src/core/include/pd_session_component.h @@ -197,6 +197,7 @@ class Genode::Pd_session_component : public Session_object<Pd_session> void map(addr_t, addr_t) override; + /**************** ** Signalling ** ****************/ @@ -334,6 +335,15 @@ class Genode::Pd_session_component : public Session_object<Pd_session> *******************************/ Managing_system_state managing_system(Managing_system_state const &) override; + + + /******************************************* + ** Support for user-level device drivers ** + *******************************************/ + + addr_t dma_addr(Ram_dataspace_capability) override; + + Attach_dma_result attach_dma(Dataspace_capability, addr_t) override; }; #endif /* _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ */ diff --git a/repos/base/src/core/include/ram_dataspace_factory.h b/repos/base/src/core/include/ram_dataspace_factory.h index 1171701260..747372b01b 100644 --- a/repos/base/src/core/include/ram_dataspace_factory.h +++ b/repos/base/src/core/include/ram_dataspace_factory.h @@ -101,6 +101,8 @@ class Genode::Ram_dataspace_factory : public Ram_allocator, static_cap_cast<Dataspace>(ds->cap()))); } + addr_t dataspace_dma_addr(Ram_dataspace_capability); + /***************************** ** Ram_allocator interface ** diff --git a/repos/base/src/core/include/region_map_component.h b/repos/base/src/core/include/region_map_component.h index b05f37c70f..203aae0fd6 100644 --- a/repos/base/src/core/include/region_map_component.h +++ b/repos/base/src/core/include/region_map_component.h @@ -28,6 +28,7 @@ #include <base/heap.h> #include <util/list.h> #include <util/fifo.h> +#include <pd_session/pd_session.h> /* core includes */ #include <platform.h> @@ -70,39 +71,40 @@ class Genode::Region_map_detach : Genode::Interface */ class Genode::Rm_region : public List<Rm_region>::Element { - private: + public: - addr_t const _base; - size_t const _size; - bool const _write; - bool const _exec; - off_t const _off; + struct Attr + { + addr_t base; + size_t size; + bool write; + bool exec; + off_t off; + bool dma; + }; + + private: Dataspace_component &_dsc; Region_map_detach &_rm; + Attr const _attr; + public: - Rm_region(addr_t base, size_t size, bool write, - Dataspace_component &dsc, off_t offset, - Region_map_detach &rm, bool exec) + Rm_region(Dataspace_component &dsc, Region_map_detach &rm, Attr attr) : - _base(base), _size(size), _write(write), _exec(exec), _off(offset), - _dsc(dsc), _rm(rm) + _dsc(dsc), _rm(rm), _attr(attr) { } - - /*************** - ** Accessors ** - ***************/ - - addr_t base() const { return _base; } - size_t size() const { return _size; } - bool write() const { return _write; } - bool executable() const { return _exec; } - Dataspace_component &dataspace() const { return _dsc; } - off_t offset() const { return _off; } - Region_map_detach &rm() const { return _rm; } + addr_t base() const { return _attr.base; } + size_t size() const { return _attr.size; } + bool write() const { return _attr.write; } + bool executable() const { return _attr.exec; } + off_t offset() const { return _attr.off; } + bool dma() const { return _attr.dma; } + Dataspace_component &dataspace() const { return _dsc; } + Region_map_detach &rm() const { return _rm; } }; @@ -358,6 +360,19 @@ class Genode::Region_map_component : private Weak_object<Region_map_component>, */ addr_t _core_local_addr(Rm_region & r); + struct Attach_attr + { + size_t size; + off_t offset; + bool use_local_addr; + addr_t local_addr; + bool executable; + bool writeable; + bool dma; + }; + + Local_addr _attach(Dataspace_capability, Attach_attr); + public: /* @@ -451,6 +466,11 @@ class Genode::Region_map_component : private Weak_object<Region_map_component>, Dataspace_component &dsc, addr_t, addr_t); + using Attach_dma_result = Pd_session::Attach_dma_result; + + Attach_dma_result attach_dma(Dataspace_capability, addr_t); + + /************************** ** Region map interface ** **************************/ diff --git a/repos/base/src/core/pd_session_component.cc b/repos/base/src/core/pd_session_component.cc index 0716b8ae6d..acc607aed4 100644 --- a/repos/base/src/core/pd_session_component.cc +++ b/repos/base/src/core/pd_session_component.cc @@ -176,3 +176,27 @@ void Pd_session_component::transfer_quota(Capability<Pd_session> pd_cap, }); } + +addr_t Pd_session_component::dma_addr(Ram_dataspace_capability ds_cap) +{ + if (_managing_system == Managing_system::DENIED) + return 0; + + if (this->cap() == ds_cap) + return 0; + + return _ram_ds_factory.dataspace_dma_addr(ds_cap); +} + + +Pd_session::Attach_dma_result +Pd_session_component::attach_dma(Dataspace_capability ds_cap, addr_t at) +{ + if (_managing_system == Managing_system::DENIED) + return Attach_dma_error::DENIED; + + if (this->cap() == ds_cap) + return Attach_dma_error::DENIED; + + return _address_space.attach_dma(ds_cap, at); +} diff --git a/repos/base/src/core/ram_dataspace_factory.cc b/repos/base/src/core/ram_dataspace_factory.cc index 9832f52a8e..04911289a0 100644 --- a/repos/base/src/core/ram_dataspace_factory.cc +++ b/repos/base/src/core/ram_dataspace_factory.cc @@ -185,3 +185,14 @@ size_t Ram_dataspace_factory::dataspace_size(Ram_dataspace_capability ds_cap) co return result; } + + +addr_t Ram_dataspace_factory::dataspace_dma_addr(Ram_dataspace_capability ds_cap) +{ + addr_t result = 0; + _ep.apply(ds_cap, [&] (Dataspace_component *c) { + if (c && c->owner(*this)) + result = c->phys_addr(); }); + + return result; +} diff --git a/repos/base/src/core/region_map_component.cc b/repos/base/src/core/region_map_component.cc index 7cf5c9d293..715708cb75 100644 --- a/repos/base/src/core/region_map_component.cc +++ b/repos/base/src/core/region_map_component.cc @@ -344,7 +344,7 @@ Mapping Region_map_component::create_map_item(Region_map_component *, .size_log2 = map_size_log2, .cached = dataspace.cacheability() == CACHED, .io_mem = dataspace.io_mem(), - .dma_buffer = dataspace.cacheability() != CACHED, + .dma_buffer = region.dma(), .write_combined = dataspace.cacheability() == WRITE_COMBINED, .writeable = region.write() && dataspace.writable(), .executable = region.executable() }; @@ -352,16 +352,13 @@ Mapping Region_map_component::create_map_item(Region_map_component *, Region_map::Local_addr -Region_map_component::attach(Dataspace_capability ds_cap, size_t size, - off_t offset, bool use_local_addr, - Region_map::Local_addr local_addr, - bool executable, bool writeable) +Region_map_component::_attach(Dataspace_capability ds_cap, Attach_attr const attr) { /* serialize access */ Mutex::Guard lock_guard(_mutex); /* offset must be positive and page-aligned */ - if (offset < 0 || align_addr(offset, get_page_size_log2()) != offset) + if (attr.offset < 0 || align_addr(attr.offset, get_page_size_log2()) != attr.offset) throw Region_conflict(); auto lambda = [&] (Dataspace_component *dsc) { @@ -374,24 +371,26 @@ Region_map_component::attach(Dataspace_capability ds_cap, size_t size, unsigned const min_align_log2 = get_page_size_log2(); - size_t const off = offset; + size_t const off = attr.offset; if (off >= dsc->size()) throw Region_conflict(); + size_t size = attr.size; + if (!size) - size = dsc->size() - offset; + size = dsc->size() - attr.offset; /* work with page granularity */ size = align_addr(size, min_align_log2); /* deny creation of regions larger then the actual dataspace */ - if (dsc->size() < size + offset) + if (dsc->size() < size + attr.offset) throw Region_conflict(); /* allocate region for attachment */ void *attach_at = nullptr; - if (use_local_addr) { - _map.alloc_addr(size, local_addr).with_result( + if (attr.use_local_addr) { + _map.alloc_addr(size, attr.local_addr).with_result( [&] (void *ptr) { attach_at = ptr; }, [&] (Range_allocator::Alloc_error error) { switch (error) { @@ -420,7 +419,7 @@ Region_map_component::attach(Dataspace_capability ds_cap, size_t size, * store. The backing store would constrain the mapping size * anyway such that a higher alignment of the region is of no use. */ - if (((dsc->map_src_addr() + offset) & ((1UL << align_log2) - 1)) != 0) + if (((dsc->map_src_addr() + attr.offset) & ((1UL << align_log2) - 1)) != 0) continue; /* try allocating the align region */ @@ -444,12 +443,19 @@ Region_map_component::attach(Dataspace_capability ds_cap, size_t size, throw Region_conflict(); } + Rm_region::Attr const region_attr + { + .base = (addr_t)attach_at, + .size = size, + .write = attr.writeable, + .exec = attr.executable, + .off = attr.offset, + .dma = attr.dma, + }; + /* store attachment info in meta data */ try { - _map.construct_metadata(attach_at, - (addr_t)attach_at, size, - dsc->writable() && writeable, - *dsc, offset, *this, executable); + _map.construct_metadata(attach_at, *dsc, *this, region_attr); } catch (Allocator_avl_tpl<Rm_region>::Assign_metadata_failed) { error("failed to store attachment info"); @@ -527,6 +533,52 @@ void Region_map_component::unmap_region(addr_t base, size_t size) } +Region_map::Local_addr +Region_map_component::attach(Dataspace_capability ds_cap, size_t size, + off_t offset, bool use_local_addr, + Region_map::Local_addr local_addr, + bool executable, bool writeable) +{ + Attach_attr const attr { + .size = size, + .offset = offset, + .use_local_addr = use_local_addr, + .local_addr = local_addr, + .executable = executable, + .writeable = writeable, + .dma = false, + }; + + return _attach(ds_cap, attr); +} + + +Region_map_component::Attach_dma_result +Region_map_component::attach_dma(Dataspace_capability ds_cap, addr_t at) +{ + Attach_attr const attr { + .size = 0, + .offset = 0, + .use_local_addr = true, + .local_addr = at, + .executable = false, + .writeable = true, + .dma = true, + }; + + using Attach_dma_error = Pd_session::Attach_dma_error; + + try { + _attach(ds_cap, attr); + return Pd_session::Attach_dma_ok(); + } + catch (Invalid_dataspace) { return Attach_dma_error::DENIED; } + catch (Region_conflict) { return Attach_dma_error::DENIED; } + catch (Out_of_ram) { return Attach_dma_error::OUT_OF_RAM; } + catch (Out_of_caps) { return Attach_dma_error::OUT_OF_CAPS; } +} + + void Region_map_component::detach(Local_addr local_addr) { /* serialize access */ diff --git a/repos/base/src/core/vm_session_common.cc b/repos/base/src/core/vm_session_common.cc index beadc25988..4f74f67e36 100644 --- a/repos/base/src/core/vm_session_common.cc +++ b/repos/base/src/core/vm_session_common.cc @@ -69,13 +69,21 @@ void Vm_session_component::attach(Dataspace_capability const cap, [&] (void *) { + Rm_region::Attr const region_attr + { + .base = guest_phys, + .size = attribute.size, + .write = dsc.writable() && attribute.writeable, + .exec = attribute.executable, + .off = (off_t)attribute.offset, + .dma = false, + }; + /* store attachment info in meta data */ try { _map.construct_metadata((void *)guest_phys, - guest_phys, attribute.size, - dsc.writable() && attribute.writeable, - dsc, attribute.offset, *this, - attribute.executable); + dsc, *this, region_attr); + } catch (Allocator_avl_tpl<Rm_region>::Assign_metadata_failed) { error("failed to store attachment info"); throw Invalid_dataspace(); diff --git a/repos/os/src/drivers/platform/legacy/x86/device_pd.cc b/repos/os/src/drivers/platform/legacy/x86/device_pd.cc index 5ffa55a2c8..88c2338a3b 100644 --- a/repos/os/src/drivers/platform/legacy/x86/device_pd.cc +++ b/repos/os/src/drivers/platform/legacy/x86/device_pd.cc @@ -31,21 +31,28 @@ void Platform::Device_pd::attach_dma_mem(Dataspace_capability ds_cap, addr_t page = ~0UL; - try { - page = _address_space.attach_at(ds_cap, dma_addr); - /* trigger eager mapping of memory */ - _pd.map(page, size); - } - catch (Out_of_ram) { throw; } - catch (Out_of_caps) { throw; } - catch (Region_map::Region_conflict) { - /* - * DMA memory already attached before. - */ - page = dma_addr; - } catch (...) { - error(_label, ": attach_at or map failed"); - } + + using Attach_dma_error = Pd_session::Attach_dma_error; + + _pd.attach_dma(ds_cap, dma_addr).with_result( + + [&] (Pd_session::Attach_dma_ok) { + + page = dma_addr; + + /* trigger eager mapping of memory */ + _pd.map(page, size); + }, + [&] (Attach_dma_error e) { + switch (e) { + case Attach_dma_error::OUT_OF_RAM: throw Out_of_ram(); + case Attach_dma_error::OUT_OF_CAPS: throw Out_of_caps(); + case Attach_dma_error::DENIED: + warning("Pd_session::attach_dma denied"); + page = dma_addr; + break; + } + }); /* sanity check */ if ((page == ~0UL) || (page != dma_addr)) { diff --git a/repos/os/src/drivers/platform/legacy/x86/device_pd.h b/repos/os/src/drivers/platform/legacy/x86/device_pd.h index 1da5981be5..3263b9c23a 100644 --- a/repos/os/src/drivers/platform/legacy/x86/device_pd.h +++ b/repos/os/src/drivers/platform/legacy/x86/device_pd.h @@ -106,7 +106,7 @@ class Platform::Device_pd Ram_quota_guard &ram_guard, Cap_quota_guard &cap_guard) : - _pd(env, "device PD", Pd_connection::Virt_space::UNCONSTRAIN), + _pd(env, Pd_connection::Device_pd()), _label(label), _address_space(env, _pd, ram_guard, cap_guard) { diff --git a/repos/os/src/drivers/platform/legacy/x86/pci_session_component.h b/repos/os/src/drivers/platform/legacy/x86/pci_session_component.h index 737734453f..764d2a291a 100644 --- a/repos/os/src/drivers/platform/legacy/x86/pci_session_component.h +++ b/repos/os/src/drivers/platform/legacy/x86/pci_session_component.h @@ -836,7 +836,7 @@ class Platform::Session_component : public Rpc_object<Session> throw Out_of_ram(); Ram_dataspace_capability ram_cap = _env_ram.alloc(size, cache); - addr_t const dma_addr = Dataspace_client(ram_cap).phys_addr(); + addr_t const dma_addr = _env.pd().dma_addr(ram_cap); if (!ram_cap.valid()) return ram_cap; @@ -868,7 +868,7 @@ class Platform::Session_component : public Rpc_object<Session> if (!ram_cap.valid() || !_owned(ram_cap)) return 0; - return Dataspace_client(ram_cap).phys_addr(); + return _env.pd().dma_addr(ram_cap); } Device_capability device(Device_name const &name) override; diff --git a/repos/os/src/drivers/platform/session_component.cc b/repos/os/src/drivers/platform/session_component.cc index 21ab0be9f4..c7b946bde6 100644 --- a/repos/os/src/drivers/platform/session_component.cc +++ b/repos/os/src/drivers/platform/session_component.cc @@ -225,10 +225,8 @@ Genode::addr_t Session_component::dma_addr(Ram_dataspace_capability ram_cap) return ret; _buffer_registry.for_each([&] (Dma_buffer & buf) { - if (buf.cap.local_name() == ram_cap.local_name()) { - Dataspace_client dsc(buf.cap); - ret = dsc.phys_addr(); - } }); + if (buf.cap.local_name() == ram_cap.local_name()) + ret = _env.pd().dma_addr(buf.cap); }); return ret; } diff --git a/repos/ports/src/app/gdb_monitor/pd_session_component.h b/repos/ports/src/app/gdb_monitor/pd_session_component.h index bc70f3d2d0..ea8e3cba7e 100644 --- a/repos/ports/src/app/gdb_monitor/pd_session_component.h +++ b/repos/ports/src/app/gdb_monitor/pd_session_component.h @@ -148,6 +148,12 @@ class Gdb_monitor::Pd_session_component : public Rpc_object<Pd_session> Managing_system_state managing_system(Managing_system_state const & state) override { return _pd.managing_system(state); } + + addr_t dma_addr(Ram_dataspace_capability ds) override { + return _pd.dma_addr(ds); } + + Attach_dma_result attach_dma(Dataspace_capability ds, addr_t at) override { + return _pd.attach_dma(ds, at); } }; #endif /* _PD_SESSION_COMPONENT_H_ */