Permalink
Browse files

Add support for allocating DMA memory

This patch extends the RAM session interface with the ability to
allocate DMA buffers. The client specifies the type of RAM dataspace to
allocate via the new 'cached' argument of the 'Ram_session::alloc()'
function. By default, 'cached' is true, which correponds to the common
case and the original behavior. When setting 'cached' to 'false', core
takes the precautions needed to register the memory as uncached in the
page table of each process that has the dataspace attached.

Currently, the support for allocating DMA buffers is implemented for
Fiasco.OC only. On x86 platforms, it is generally not needed. But on
platforms with more relaxed cache coherence (such as ARM), user-level
device drivers should always use uncacheable memory for DMA transactions.
  • Loading branch information...
1 parent 896d12d commit 288fd4e56e636a0b3eb193353cf8028614a018b7 @nfeske nfeske committed Jun 18, 2012
@@ -71,6 +71,8 @@ namespace Genode {
return Fiasco::l4_fpage(_src_addr, _log2size, rights);
}
+ bool write_combined() const { return _write_combined; }
+
/**
* Prepare map operation
*
@@ -83,6 +83,13 @@ void Ipc_pager::reply_and_wait_for_fault()
l4_umword_t grant = _reply_mapping.grant() ? L4_MAP_ITEM_GRANT : 0;
l4_utcb_mr()->mr[0] = _reply_mapping.dst_addr() | L4_ITEM_MAP | grant;
+
+ /*
+ * XXX Does L4_FPAGE_BUFFERABLE imply L4_FPAGE_UNCACHEABLE?
+ */
+ if (_reply_mapping.write_combined())
+ l4_utcb_mr()->mr[0] |= L4_FPAGE_BUFFERABLE << 4;
+
l4_utcb_mr()->mr[1] = _reply_mapping.fpage().raw;
_tag = l4_ipc_send_and_wait(_last, l4_utcb(), snd_tag,
@@ -22,12 +22,15 @@
/* Fiasco includes */
namespace Fiasco {
#include <l4/sys/ipc.h>
+#include <l4/sigma0/sigma0.h>
+#include <l4/sys/task.h>
+#include <l4/sys/cache.h>
}
namespace Genode {
/**
- * Map page locally within core
+ * Map pages locally within core
*
* On Fiasco, all mapping originate from virtual addresses. At startup,
* core obtains the whole memory sigma0 in a one-to-one fashion. Hence,
@@ -60,6 +63,83 @@ namespace Genode {
return true;
}
+
+
+ static inline bool can_use_super_page(addr_t base, size_t size)
+ {
+ return (base & (get_super_page_size() - 1)) == 0
+ && (size >= get_super_page_size());
+ }
+
+
+ /**
+ * Map memory-mapped I/O range within core
+ *
+ * \return true on success
+ */
+ static inline bool map_local_io(addr_t from_addr, addr_t to_addr,
+ size_t num_pages)
+ {
+ using namespace Fiasco;
+
+ size_t size = num_pages << get_page_size_log2();
+
+ /* call sigma0 for I/O region */
+ unsigned offset = 0;
+ while (size) {
+ /* FIXME what about caching demands? */
+ /* FIXME what about read / write? */
+
+ l4_utcb_mr()->mr[0] = SIGMA0_REQ_FPAGE_IOMEM;
+
+ size_t page_size_log2 = get_page_size_log2();
+ if (can_use_super_page(from_addr + offset, size))
+ page_size_log2 = get_super_page_size_log2();
+ l4_utcb_mr()->mr[1] = l4_fpage(from_addr + offset,
+ page_size_log2, L4_FPAGE_RWX).raw;
+
+ /* open receive window for mapping */
+ l4_utcb_br()->bdr = 0;
+ l4_utcb_br()->br[0] = L4_ITEM_MAP;
+ l4_utcb_br()->br[1] = l4_fpage((addr_t)to_addr + offset,
+ page_size_log2, L4_FPAGE_RWX).raw;
+
+ l4_msgtag_t tag = l4_msgtag(L4_PROTO_SIGMA0, 2, 0, 0);
+ tag = l4_ipc_call(L4_BASE_PAGER_CAP, l4_utcb(), tag, L4_IPC_NEVER);
+ if (l4_ipc_error(tag, l4_utcb())) {
+ PERR("Ipc error %ld", l4_ipc_error(tag, l4_utcb()));
+ return false;
+ }
+
+ if (l4_msgtag_items(tag) < 1) {
+ PERR("Got no mapping!");
+ return false;
+ }
+
+ offset += 1 << page_size_log2;
+ size -= 1 << page_size_log2;
+ }
+ return true;
+ }
+
+
+ static inline void unmap_local(addr_t local_base, size_t num_pages)
+ {
+ using namespace Fiasco;
+
+ size_t size = num_pages << get_page_size_log2();
+ addr_t addr = local_base;
+
+ /*
+ * XXX divide operation into flexpages greater than page size
+ */
+ for (; addr < local_base + size; addr += L4_PAGESIZE)
+ l4_task_unmap(L4_BASE_TASK_CAP,
+ l4_fpage(addr, L4_LOG2_PAGESIZE, L4_FPAGE_RW),
+ L4_FP_OTHER_SPACES);
+
+ l4_cache_clean_data(local_base, local_base + size);
+ }
}
#endif /* _CORE__INCLUDE__MAP_LOCAL_H_ */
@@ -16,12 +16,7 @@
#include <platform.h>
#include <util.h>
#include <io_mem_session_component.h>
-
-/* Fiasco includes */
-namespace Fiasco {
-#include <l4/sys/ipc.h>
-#include <l4/sigma0/sigma0.h>
-}
+#include <map_local.h>
using namespace Genode;
@@ -32,17 +27,8 @@ void Io_mem_session_component::_unmap_local(addr_t base, size_t size)
}
-static inline bool can_use_super_page(addr_t base, size_t size)
-{
- return (base & (get_super_page_size() - 1)) == 0
- && (size >= get_super_page_size());
-}
-
-
addr_t Io_mem_session_component::_map_local(addr_t base, size_t size)
{
- using namespace Fiasco;
-
/* align large I/O dataspaces on a super-page boundary within core */
size_t alignment = (size >= get_super_page_size()) ? get_super_page_size_log2()
: get_page_size_log2();
@@ -52,40 +38,9 @@ addr_t Io_mem_session_component::_map_local(addr_t base, size_t size)
if (!platform()->region_alloc()->alloc_aligned(size, &local_base, alignment))
return 0;
- /* call sigma0 for I/O region */
- unsigned offset = 0;
- while (size) {
- /* FIXME what about caching demands? */
- /* FIXME what about read / write? */
-
- l4_utcb_mr()->mr[0] = SIGMA0_REQ_FPAGE_IOMEM;
-
- size_t page_size_log2 = get_page_size_log2();
- if (can_use_super_page(base + offset, size))
- page_size_log2 = get_super_page_size_log2();
- l4_utcb_mr()->mr[1] = l4_fpage(base + offset,
- page_size_log2, L4_FPAGE_RWX).raw;
-
- /* open receive window for mapping */
- l4_utcb_br()->bdr = 0;
- l4_utcb_br()->br[0] = L4_ITEM_MAP;
- l4_utcb_br()->br[1] = l4_fpage((addr_t)local_base + offset,
- page_size_log2, L4_FPAGE_RWX).raw;
-
- l4_msgtag_t tag = l4_msgtag(L4_PROTO_SIGMA0, 2, 0, 0);
- tag = l4_ipc_call(L4_BASE_PAGER_CAP, l4_utcb(), tag, L4_IPC_NEVER);
- if (l4_ipc_error(tag, l4_utcb())) {
- PERR("Ipc error %ld", l4_ipc_error(tag, l4_utcb()));
- return 0;
- }
-
- if (l4_msgtag_items(tag) < 1) {
- PERR("Got no mapping!");
- return 0;
- }
-
- offset += 1 << page_size_log2;
- size -= 1 << page_size_log2;
+ if (!map_local_io(base, (addr_t)local_base, size >> get_page_size_log2())) {
+ PERR("map_local_io failed\n");
+ return 0;
}
return (addr_t)local_base;
@@ -2,9 +2,6 @@
* \brief Export RAM dataspace as shared memory object (dummy)
* \author Norman Feske
* \date 2006-07-03
- *
- * On L4, each dataspace _is_ a shared memory object.
- * Therefore, these functions are empty.
*/
/*
@@ -14,14 +11,26 @@
* under the terms of the GNU General Public License version 2.
*/
-#include "ram_session_component.h"
+/* core-local includes */
+#include <ram_session_component.h>
+#include <map_local.h>
+
+namespace Fiasco {
+#include <l4/sys/cache.h>
+}
using namespace Genode;
void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { }
void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { }
+
void Ram_session_component::_clear_ds(Dataspace_component *ds)
{
memset((void *)ds->phys_addr(), 0, ds->size());
+
+ if (ds->write_combined())
+ Fiasco::l4_cache_clean_data((Genode::addr_t)ds->phys_addr(),
+ (Genode::addr_t)ds->phys_addr() + ds->size());
}
+
@@ -13,23 +13,12 @@
/* core includes */
#include <rm_session_component.h>
-
-/* Fiasco includes */
-namespace Fiasco {
-#include <l4/sys/task.h>
-}
+#include <map_local.h>
using namespace Genode;
-
void Rm_client::unmap(addr_t core_local_base, addr_t virt_base, size_t size)
{
- using namespace Fiasco;
-
// TODO unmap it only from target space
- addr_t addr = core_local_base;
- for (; addr < core_local_base + size; addr += L4_PAGESIZE)
- l4_task_unmap(L4_BASE_TASK_CAP,
- l4_fpage(addr, L4_LOG2_PAGESIZE, L4_FPAGE_RW),
- L4_FP_OTHER_SPACES);
+ unmap_local(core_local_base, size >> get_page_size_log2());
}
@@ -55,7 +55,7 @@ class Context_area_ram_session : public Genode::Ram_session
{
public:
- Genode::Ram_dataspace_capability alloc(Genode::size_t size) {
+ Genode::Ram_dataspace_capability alloc(Genode::size_t size, bool) {
return Genode::Ram_dataspace_capability(); }
void free(Genode::Ram_dataspace_capability) { }
@@ -252,12 +252,12 @@ namespace Genode {
Expanding_ram_session_client(Ram_session_capability cap)
: Ram_session_client(cap), _cap(cap) { }
- Ram_dataspace_capability alloc(size_t size) {
+ Ram_dataspace_capability alloc(size_t size, bool cached) {
bool try_again;
do {
try_again = false;
try {
- return Ram_session_client::alloc(size);
+ return Ram_session_client::alloc(size, cached);
} catch (Ram_session::Out_of_metadata) {
@@ -78,7 +78,7 @@ class Context_area_ram_session : public Genode::Ram_session
{
public:
- Genode::Ram_dataspace_capability alloc(Genode::size_t size) {
+ Genode::Ram_dataspace_capability alloc(Genode::size_t size, bool) {
return Genode::Ram_dataspace_capability(); }
void free(Genode::Ram_dataspace_capability) { }
@@ -50,8 +50,9 @@ namespace Genode {
/**
* Constructor
*/
- Dataspace_component(size_t size, addr_t addr, bool writable,
- Dataspace_owner * owner = 0)
+ Dataspace_component(size_t size, addr_t addr,
+ bool /* write_combined */, bool writable,
+ Dataspace_owner * owner)
: _size(size), _addr(addr), _writable(writable),
_owner(owner) { }
@@ -67,7 +68,7 @@ namespace Genode {
*/
Dataspace_component(size_t size, addr_t core_local_addr,
addr_t phys_addr, bool write_combined,
- bool writable, Dataspace_owner * _owner = 0)
+ bool writable, Dataspace_owner * _owner)
: _size(size), _addr(phys_addr), _owner(_owner)
{
PWRN("Should only be used for IOMEM and not within Linux.");
@@ -62,7 +62,7 @@ Rom_session_component::Rom_session_component(Rom_fs *rom_fs,
if (fsize == 0)
throw Root::Invalid_args();
- _ds = Dataspace_component(fsize, 0, false);
+ _ds = Dataspace_component(fsize, 0, false, false, 0);
_ds.fname(fname_buf);
Dataspace_capability ds_cap = _ds_ep->manage(&_ds);
@@ -82,7 +82,7 @@ class Context_area_ram_session : public Ram_session
{
public:
- Ram_dataspace_capability alloc(size_t size)
+ Ram_dataspace_capability alloc(size_t size, bool)
{
/* find free context */
unsigned i;
@@ -83,12 +83,12 @@ namespace Genode {
Expanding_ram_session_client(Ram_session_capability cap)
: Ram_session_client(cap), _cap(cap) { }
- Ram_dataspace_capability alloc(size_t size) {
+ Ram_dataspace_capability alloc(size_t size, bool cached) {
bool try_again;
do {
try_again = false;
try {
- return Ram_session_client::alloc(size);
+ return Ram_session_client::alloc(size, cached);
} catch (Ram_session::Out_of_metadata) {
@@ -25,8 +25,8 @@ namespace Genode {
explicit Ram_session_client(Ram_session_capability session)
: Rpc_client<Ram_session>(session) { }
- Ram_dataspace_capability alloc(size_t size) {
- return call<Rpc_alloc>(size); }
+ Ram_dataspace_capability alloc(size_t size, bool cached = true) {
+ return call<Rpc_alloc>(size, cached); }
void free(Ram_dataspace_capability ds) { call<Rpc_free>(ds); }
@@ -48,13 +48,16 @@ namespace Genode {
/**
* Allocate RAM dataspace
*
- * \param size size of RAM dataspace
+ * \param size size of RAM dataspace
+ * \param cached true for cached memory, false for allocating
+ * uncached memory, i.e., for DMA buffers
*
* \throw Quota_exceeded
* \throw Out_of_metadata
* \return capability to new RAM dataspace
*/
- virtual Ram_dataspace_capability alloc(size_t size) = 0;
+ virtual Ram_dataspace_capability alloc(size_t size,
+ bool cached = true) = 0;
/**
* Free RAM dataspace
@@ -112,7 +115,8 @@ namespace Genode {
*********************/
GENODE_RPC_THROW(Rpc_alloc, Ram_dataspace_capability, alloc,
- GENODE_TYPE_LIST(Quota_exceeded, Out_of_metadata), size_t);
+ GENODE_TYPE_LIST(Quota_exceeded, Out_of_metadata),
+ size_t, bool);
GENODE_RPC(Rpc_free, void, free, Ram_dataspace_capability);
GENODE_RPC(Rpc_ref_account, int, ref_account, Ram_session_capability);
GENODE_RPC(Rpc_transfer_quota, int, transfer_quota, Ram_session_capability, size_t);
Oops, something went wrong.

0 comments on commit 288fd4e

Please sign in to comment.