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.
This commit is contained in:
Norman Feske 2012-06-18 15:20:31 +02:00
parent 896d12d0b8
commit 288fd4e56e
28 changed files with 187 additions and 124 deletions

View File

@ -71,6 +71,8 @@ namespace Genode {
return Fiasco::l4_fpage(_src_addr, _log2size, rights);
}
bool write_combined() const { return _write_combined; }
/**
* Prepare map operation
*

View File

@ -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,

View File

@ -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_ */

View File

@ -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;

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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) { }

View File

@ -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) {

View File

@ -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) { }

View File

@ -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.");

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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); }

View File

@ -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);

View File

@ -88,7 +88,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 cached)
{
/* find free context */
unsigned i;
@ -111,7 +111,7 @@ class Context_area_ram_session : public Ram_session
}
context_ds[i] = new (platform()->core_mem_alloc())
Dataspace_component(size, 0, (addr_t)phys_base, false, true);
Dataspace_component(size, 0, (addr_t)phys_base, false, true, 0);
Dataspace_capability cap = Dataspace_capability::local_cap(context_ds[i]);
return static_cap_cast<Ram_dataspace>(cap);

View File

@ -68,10 +68,10 @@ namespace Genode {
** RAM-session interface **
***************************/
Ram_dataspace_capability alloc(size_t size)
Ram_dataspace_capability alloc(size_t size, bool cached)
{
Lock::Guard lock_guard(_lock);
return RAM_SESSION_IMPL::alloc(size);
return RAM_SESSION_IMPL::alloc(size, cached);
}
void free(Ram_dataspace_capability ds)

View File

@ -38,19 +38,20 @@ namespace Genode {
{
private:
addr_t _phys_addr; /* address of dataspace in physical memory */
addr_t _core_local_addr; /* address of core-local mapping */
size_t _size; /* size of dataspace in bytes */
bool _is_io_mem; /* dataspace is I/O mem, not to be touched */
bool _write_combined; /* access I/O memory write-combined */
bool _writable; /* false if dataspace is read-only */
addr_t const _phys_addr; /* address of dataspace in physical memory */
addr_t _core_local_addr; /* address of core-local mapping */
size_t const _size; /* size of dataspace in bytes */
bool const _is_io_mem; /* dataspace is I/O mem, not to be touched */
bool const _write_combined; /* access I/O memory write-combined, or
RAM uncacheable respectively */
bool const _writable; /* false if dataspace is read-only */
List<Rm_region> _regions; /* regions this is attached to */
Lock _lock;
/* Holds the dataspace owner if a distinction between owner and
* others is necessary on the dataspace, otherwise it is 0 */
Dataspace_owner * _owner;
Dataspace_owner const * _owner;
protected:
@ -69,9 +70,10 @@ namespace Genode {
* Default constructor returning an invalid dataspace
*/
Dataspace_component()
: _phys_addr(0), _core_local_addr(0), _size(0),
_is_io_mem(false), _write_combined(false), _writable(false),
_owner(0), _managed(false) { }
:
_phys_addr(0), _core_local_addr(0), _size(0),
_is_io_mem(false), _write_combined(false), _writable(false),
_owner(0), _managed(false) { }
/**
* Constructor for non-I/O dataspaces
@ -79,11 +81,13 @@ namespace Genode {
* This constructor is used by RAM and ROM dataspaces.
*/
Dataspace_component(size_t size, addr_t core_local_addr,
bool writable,
Dataspace_owner * owner = 0)
: _phys_addr(core_local_addr), _core_local_addr(core_local_addr),
_size(round_page(size)), _is_io_mem(false), _write_combined(false),
_writable(writable), _owner(owner), _managed(false) { }
bool write_combined, bool writable,
Dataspace_owner *owner)
:
_phys_addr(core_local_addr), _core_local_addr(core_local_addr),
_size(round_page(size)), _is_io_mem(false),
_write_combined(write_combined), _writable(writable),
_owner(owner), _managed(false) { }
/**
* Constructor for dataspaces with different core-local and
@ -97,11 +101,11 @@ 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)
: _phys_addr(phys_addr), _core_local_addr(core_local_addr),
_size(size), _is_io_mem(true), _write_combined(write_combined),
_writable(writable), _owner(owner), _managed(false) { }
bool writable, Dataspace_owner *owner)
:
_phys_addr(phys_addr), _core_local_addr(core_local_addr),
_size(size), _is_io_mem(true), _write_combined(write_combined),
_writable(writable), _owner(owner), _managed(false) { }
/**
* Destructor

View File

@ -76,7 +76,7 @@ namespace Genode {
:
Dataspace_component(da.size, da.core_local_addr,
da.phys_addr, da.write_combined,
true),
true, 0),
req_base(da.req_base) { }

View File

@ -103,7 +103,6 @@ namespace Genode {
*/
void _clear_ds(Dataspace_component *ds);
public:
/**
@ -135,13 +134,11 @@ namespace Genode {
*/
~Ram_session_component();
/**
* Accessors
*/
Ram_session_component *ref_account() { return _ref_account; }
/**
* Register quota donation at allocator guard
*/
@ -152,7 +149,7 @@ namespace Genode {
** RAM Session interface **
***************************/
Ram_dataspace_capability alloc(size_t);
Ram_dataspace_capability alloc(size_t, bool);
void free(Ram_dataspace_capability);
int ref_account(Ram_session_capability);
int transfer_quota(Ram_session_capability, size_t);

View File

@ -244,8 +244,9 @@ namespace Genode {
/**
* Constructor
*/
Rm_dataspace_component(Rm_session_component *rsc, size_t size) :
Dataspace_component(size, 0, false),
Rm_dataspace_component(Rm_session_component *rsc, size_t size)
:
Dataspace_component(size, 0, false, false, 0),
_rm_session_component(rsc) { _managed = true; }

View File

@ -97,7 +97,7 @@ void Ram_session_component::_remove_ref_account_member(Ram_session_component *me
}
Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size)
Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, bool cached)
{
/* zero-sized dataspaces are not allowed */
if (!ds_size) return Ram_dataspace_capability();
@ -154,13 +154,23 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size)
Dataspace_component *ds;
try {
ds = new (&_ds_slab) Dataspace_component(ds_size, (addr_t)ds_addr, true, this);
/*
* For non-cached RAM dataspaces, we mark the dataspace as write
* combined and expect the pager to evaluate this dataspace property
* when resolving page faults.
*/
ds = new (&_ds_slab)
Dataspace_component(ds_size, (addr_t)ds_addr, !cached, true, this);
} catch (Allocator::Out_of_memory) {
PWRN("Could not allocate metadata");
throw Out_of_metadata();
}
/* fill new dataspaces with zeros */
/*
* Fill new dataspaces with zeros. For non-cached RAM dataspaces, this
* function must also make sure to flush all cache lines related to the
* address range used by the dataspace.
*/
_clear_ds(ds);
/* keep track of the used quota for actual payload */

View File

@ -25,7 +25,7 @@ Rom_session_component::Rom_session_component(Rom_fs *rom_fs,
:
_rom_module(_find_rom(rom_fs, args)),
_ds(_rom_module ? _rom_module->size() : 0,
_rom_module ? _rom_module->addr() : 0, false),
_rom_module ? _rom_module->addr() : 0, false, false, 0),
_ds_ep(ds_ep)
{
/* ROM module not found */

View File

@ -111,6 +111,8 @@ int Framebuffer_drv::map_io_mem(addr_t base, size_t size, bool write_combined,
return -3;
}
PDBG("fb mapped to %p", *out_addr);
if (out_io_ds)
*out_io_ds = io_ds;

View File

@ -34,7 +34,7 @@ namespace Genode {
Ram_session_client_guard(Ram_session_capability session, size_t amount)
: Ram_session_client(session), _amount(amount), _consumed(0) { }
Ram_dataspace_capability alloc(size_t size)
Ram_dataspace_capability alloc(size_t size, bool cached)
{
Lock::Guard _consumed_lock_guard(_consumed_lock);
@ -44,7 +44,8 @@ namespace Genode {
return Ram_dataspace_capability();
}
Ram_dataspace_capability cap = Ram_session_client::alloc(size);
Ram_dataspace_capability cap =
Ram_session_client::alloc(size, cached);
_consumed += size;

View File

@ -29,7 +29,7 @@ Ram_session_component::~Ram_session_component()
{ }
Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size)
Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, bool cached)
{
return _parent_ram_session.alloc(ds_size);
}

View File

@ -43,7 +43,7 @@ class Ram_session_component : public Rpc_object<Ram_session>
** RAM Session interface **
***************************/
Ram_dataspace_capability alloc(Genode::size_t);
Ram_dataspace_capability alloc(Genode::size_t, bool);
void free(Ram_dataspace_capability);
int ref_account(Ram_session_capability);
int transfer_quota(Ram_session_capability, Genode::size_t);

View File

@ -135,9 +135,10 @@ namespace Noux {
** Ram_session interface **
***************************/
Ram_dataspace_capability alloc(size_t size)
Ram_dataspace_capability alloc(size_t size, bool cached)
{
Ram_dataspace_capability ds_cap = env()->ram_session()->alloc(size);
Ram_dataspace_capability ds_cap =
env()->ram_session()->alloc(size, cached);
Ram_dataspace_info *ds_info = new (env()->heap())
Ram_dataspace_info(ds_cap);