genode/repos/dde_bsd/src/lib/audio/mem.cc

413 lines
9.0 KiB
C++

/**
* \brief Audio driver BSD API emulation
* \author Josef Soentgen
* \date 2014-11-16
*/
/*
* Copyright (C) 2014-2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/allocator_avl.h>
#include <base/env.h>
#include <base/log.h>
#include <dataspace/client.h>
#include <rm_session/connection.h>
#include <region_map/client.h>
#include <util/string.h>
/* local includes */
#include <bsd.h>
#include <bsd_emul.h>
static bool const verbose = false;
namespace Bsd {
typedef Genode::addr_t addr_t;
class Slab_backend_alloc;
class Slab_alloc;
class Malloc;
}
/**
* Back-end allocator for Genode's slab allocator
*/
class Bsd::Slab_backend_alloc : public Genode::Allocator,
public Genode::Rm_connection,
public Genode::Region_map_client
{
private:
enum {
VM_SIZE = 8 * 1024 * 1024, /* size of VM region to reserve */
BLOCK_SIZE = 1024 * 1024, /* 1 MiB */
ELEMENTS = VM_SIZE / BLOCK_SIZE, /* MAX number of dataspaces in VM */
};
addr_t _base; /* virt. base address */
Genode::Ram_dataspace_capability _ds_cap[ELEMENTS]; /* dataspaces to put in VM */
addr_t _ds_phys[ELEMENTS]; /* physical bases of dataspaces */
int _index; /* current index in ds_cap */
Genode::Allocator_avl _range; /* manage allocations */
Genode::Ram_session &_ram; /* ram session to allocate ds from */
bool _alloc_block()
{
if (_index == ELEMENTS) {
Genode::error("Slab-backend exhausted!");
return false;
}
try {
_ds_cap[_index] = _ram.alloc(BLOCK_SIZE);
Region_map_client::attach_at(_ds_cap[_index], _index * BLOCK_SIZE, BLOCK_SIZE, 0);
} catch (...) { return false; }
/* return base + offset in VM area */
addr_t block_base = _base + (_index * BLOCK_SIZE);
++_index;
_range.add_range(block_base, BLOCK_SIZE);
return true;
}
public:
Slab_backend_alloc(Genode::Ram_session &ram, Genode::Region_map &rm,
Genode::Allocator &md_alloc)
:
Region_map_client(Rm_connection::create(VM_SIZE)),
_index(0), _range(&md_alloc), _ram(ram)
{
/* reserver attach us, anywere */
_base = rm.attach(dataspace());
}
addr_t start() const { return _base; }
addr_t end() const { return _base + VM_SIZE - 1; }
/*************************
** Allocator interface **
*************************/
bool alloc(Genode::size_t size, void **out_addr)
{
bool done = _range.alloc(size, out_addr);
if (done)
return done;
done = _alloc_block();
if (!done) {
Genode::error("Backend allocator exhausted\n");
return false;
}
return _range.alloc(size, out_addr);
}
void free(void *addr, Genode::size_t size) { _range.free(addr, size); }
Genode::size_t overhead(Genode::size_t size) const { return 0; }
bool need_size_for_free() const override { return false; }
};
/**
* Slab allocator using our back-end allocator
*/
class Bsd::Slab_alloc : public Genode::Slab
{
private:
Genode::size_t const _object_size;
static Genode::size_t _calculate_block_size(Genode::size_t object_size)
{
Genode::size_t const block_size = 16*object_size;
return Genode::align_addr(block_size, 12);
}
public:
Slab_alloc(Genode::size_t object_size, Slab_backend_alloc &allocator)
:
Slab(object_size, _calculate_block_size(object_size), 0, &allocator),
_object_size(object_size)
{ }
Genode::addr_t alloc()
{
Genode::addr_t result;
return (Slab::alloc(_object_size, (void **)&result) ? result : 0);
}
void free(void *ptr) { Slab::free(ptr, _object_size); }
};
/**
* Memory interface
*/
class Bsd::Malloc
{
private:
enum {
SLAB_START_LOG2 = 5, /* 32 B */
SLAB_STOP_LOG2 = 16, /* 64 KiB */
NUM_SLABS = (SLAB_STOP_LOG2 - SLAB_START_LOG2) + 1,
};
typedef Genode::addr_t addr_t;
typedef Bsd::Slab_alloc Slab_alloc;
typedef Bsd::Slab_backend_alloc Slab_backend_alloc;
Slab_backend_alloc &_back_allocator;
Slab_alloc *_allocator[NUM_SLABS];
addr_t _start;
addr_t _end;
/**
* Set 'value' at 'addr'
*/
void _set_at(addr_t addr, addr_t value) { *((addr_t *)addr) = value; }
/**
* Retrieve slab index belonging to given address
*/
unsigned _slab_index(Genode::addr_t **addr)
{
using namespace Genode;
/* get index */
addr_t index = *(*addr - 1);
/*
* If index large, we use aligned memory, retrieve beginning of slab entry
* and read index from there
*/
if (index > 32) {
*addr = (addr_t *)*(*addr - 1);
index = *(*addr - 1);
}
return index;
}
/**
* Get the originally requested size of the allocation
*/
Genode::size_t _get_orig_size(Genode::addr_t **addr)
{
using namespace Genode;
addr_t index = *(*addr - 1);
if (index > 32) {
*addr = (addr_t *) * (*addr - 1);
}
return *(*addr - 2);
}
public:
Malloc(Slab_backend_alloc &alloc, Genode::Allocator &md_alloc)
:
_back_allocator(alloc), _start(alloc.start()),
_end(alloc.end())
{
/* init slab allocators */
for (unsigned i = SLAB_START_LOG2; i <= SLAB_STOP_LOG2; i++) {
_allocator[i - SLAB_START_LOG2] =
new (&md_alloc) Slab_alloc(1U << i, _back_allocator);
}
}
/**
* Alloc in slabs
*/
void *alloc(Genode::size_t size, int align = 0)
{
using namespace Genode;
/* save requested size */
Genode::size_t orig_size = size;
size += sizeof(addr_t);
/* += slab index + aligment size */
size += sizeof(addr_t) + (align > 2 ? (1 << align) : 0);
int msb = Genode::log2(size);
if (size > (1U << msb))
msb++;
if (size < (1U << SLAB_START_LOG2))
msb = SLAB_STOP_LOG2;
if (msb > SLAB_STOP_LOG2) {
Genode::error("Slab too large ", 1U << msb, " reqested ", size);
return 0;
}
addr_t addr = _allocator[msb - SLAB_START_LOG2]->alloc();
if (!addr) {
Genode::error("Failed to get slab for ", 1U << msb);
return 0;
}
_set_at(addr, orig_size);
addr += sizeof(addr_t);
_set_at(addr, msb - SLAB_START_LOG2);
addr += sizeof(addr_t);
if (align > 2) {
/* save */
addr_t ptr = addr;
addr_t align_val = (1U << align);
addr_t align_mask = align_val - 1;
/* align */
addr = (addr + align_val) & ~align_mask;
/* write start address before aligned address */
_set_at(addr - sizeof(addr_t), ptr);
}
return (addr_t *)addr;
}
void free(void const *a)
{
using namespace Genode;
addr_t *addr = (addr_t *)a;
unsigned nr = _slab_index(&addr);
/* we need to decrease addr by 2, orig_size and index come first */
_allocator[nr]->free((void *)(addr - 2));
}
Genode::size_t size(void const *a)
{
using namespace Genode;
addr_t *addr = (addr_t *)a;
return _get_orig_size(&addr);
}
bool inside(addr_t const addr) const { return (addr > _start) && (addr <= _end); }
};
static Bsd::Malloc *_malloc;
void Bsd::mem_init(Genode::Env &env, Genode::Allocator &alloc)
{
static Bsd::Slab_backend_alloc sb(env.ram(), env.rm(), alloc);
static Bsd::Malloc m(sb, alloc);
_malloc = &m;
}
static Bsd::Malloc& malloc_backend() { return *_malloc; }
/**********************
** Memory allocation *
**********************/
extern "C" void *malloc(size_t size, int type, int flags)
{
void *addr = malloc_backend().alloc(size);
if (flags & M_ZERO)
Genode::memset(addr, 0, size);
return addr;
}
extern "C" void *mallocarray(size_t nmemb, size_t size, int type, int flags)
{
if (size != 0 && nmemb > (~0UL / size))
return 0;
return malloc(nmemb * size, type, flags);
}
extern "C" void free(void *addr, int type, size_t size)
{
if (!addr) return;
if (!malloc_backend().inside((Genode::addr_t)addr)) {
Genode::error("cannot free unknown memory at ", __builtin_return_address(0),
" called from ", addr);
return;
}
if (size) {
size_t ssize = malloc_backend().size(addr);
if (ssize != size) {
Genode::warning("size: ", size, "for ", addr,
" does not match stored size: ", ssize);
}
}
malloc_backend().free(addr);
}
/*****************
** sys/systm.h **
*****************/
extern "C" void bzero(void *b, size_t len)
{
if (b == nullptr) return;
Genode::memset(b, 0, len);
}
extern "C" void bcopy(const void *src, void *dst, size_t len)
{
/* XXX may overlap */
Genode::memcpy(dst, src, len);
}
extern "C" int uiomove(void *buf, int n, struct uio *uio)
{
void *dst = nullptr;
void *src = nullptr;
size_t len = uio->uio_resid < (size_t)n ? uio->uio_resid : (size_t)n;
switch (uio->uio_rw) {
case UIO_READ:
dst = buf;
src = ((char*)uio->buf) + uio->uio_offset;
break;
case UIO_WRITE:
dst = ((char*)uio->buf) + uio->uio_offset;
src = buf;
break;
}
Genode::memcpy(dst, src, len);
uio->uio_resid -= len;
uio->uio_offset += len;
return 0;
}