genode/repos/libports/src/lib/acpica/iomem.cc
Norman Feske b44f0554bd Adapt high-level components to new parent API
This patch adjusts the various users of the 'Child' API to the changes
on the account of the new non-blocking parent interface. It also removes
the use of the no-longer-available 'Connection::KEEP_OPEN' feature.

With the adjustment, we took the opportunity to redesign several
components to fit the non-blocking execution model much better, in
particular the demo applications.

Issue #2120
2016-11-30 13:37:03 +01:00

294 lines
6.8 KiB
C++

/*
* \brief I/O memory backend for ACPICA library
* \author Alexander Boettcher
* \date 2016-11-14
*/
/*
* Copyright (C) 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.
*/
#include <base/log.h>
#include <util/misc_math.h>
#include <io_mem_session/connection.h>
#include <rm_session/connection.h>
#include "env.h"
extern "C" {
#include "acpi.h"
#include "acpiosxf.h"
}
#define FAIL(retval) \
{ \
Genode::error(__func__, ":", __LINE__, " called - dead"); \
Genode::Lock lock; \
while (1) lock.lock(); \
return retval; \
}
namespace Acpica { class Io_mem; };
class Acpica::Io_mem
{
private:
ACPI_PHYSICAL_ADDRESS _phys;
ACPI_SIZE _size;
Genode::uint8_t *_virt = nullptr;
Genode::Io_mem_connection *_io_mem = nullptr;
unsigned _ref;
static Acpica::Io_mem _ios[16];
public:
bool valid() const { return _io_mem != nullptr; }
bool refs() const { return _ref != ~0U; }
bool contains_virt (const Genode::uint8_t * v, const ACPI_SIZE s) const
{
return _virt <= v && v + s <= _virt + _size;
}
bool contains_phys (const ACPI_PHYSICAL_ADDRESS p, const ACPI_SIZE s) const
{
return _phys <= p && p + s <= _phys + _size;
}
Genode::addr_t to_virt(ACPI_PHYSICAL_ADDRESS p) {
_ref ++;
return reinterpret_cast<Genode::addr_t>(_virt + (p - _phys));
}
bool ref_dec() { return --_ref; }
template <typename FUNC>
static void apply_to_all(FUNC const &func = [] () { } )
{
for (unsigned i = 0; i < sizeof(_ios) / sizeof(_ios[0]); i++)
func(_ios[i]);
}
void invalidate(ACPI_SIZE s)
{
if (_io_mem && refs())
Genode::destroy(Acpica::heap(), _io_mem);
ACPI_PHYSICAL_ADDRESS const p = _phys;
_phys = _size = 0;
_virt = nullptr;
_io_mem = nullptr;
if (refs())
return;
_ref = 0;
/**
* Continue in order to look for the larger entry that replaced
* this one. Required to decrement ref count.
*/
apply_to_all([&] (Acpica::Io_mem &io_mem) {
if (!io_mem.contains_phys(p, s) || !io_mem.refs())
return;
if (io_mem.ref_dec())
return;
io_mem.invalidate(s);
});
}
template <typename FUNC>
static Genode::addr_t apply_u(FUNC const &func = [] () { } )
{
for (unsigned i = 0; i < sizeof(_ios) / sizeof(_ios[0]); i++)
{
Genode::addr_t r = func(_ios[i]);
if (r) return r;
}
return 0UL;
}
template <typename FUNC>
static Acpica::Io_mem * apply_p(FUNC const &func = [] () { } )
{
for (unsigned i = 0; i < sizeof(_ios) / sizeof(_ios[0]); i++)
{
Acpica::Io_mem * r = func(_ios[i]);
if (r) return r;
}
return nullptr;
}
static Acpica::Io_mem * allocate(ACPI_PHYSICAL_ADDRESS p, ACPI_SIZE s,
unsigned r)
{
return Acpica::Io_mem::apply_p([&] (Acpica::Io_mem &io_mem) {
if (io_mem.valid())
return reinterpret_cast<Acpica::Io_mem *>(0);
io_mem._phys = p & ~0xFFFUL;
io_mem._size = Genode::align_addr(p + s - io_mem._phys, 12);
io_mem._ref = r;
io_mem._virt = 0;
io_mem._io_mem = new (Acpica::heap())
Genode::Io_mem_connection(io_mem._phys, io_mem._size);
return &io_mem;
});
}
static Genode::addr_t insert(ACPI_PHYSICAL_ADDRESS p, ACPI_SIZE s)
{
Acpica::Io_mem * io_mem = allocate(p, s, 1);
if (!io_mem)
return 0UL;
io_mem->_virt = Acpica::env().rm().attach(io_mem->_io_mem->dataspace(),
io_mem->_size);
return reinterpret_cast<Genode::addr_t>(io_mem->_virt);
}
Genode::addr_t pre_expand(ACPI_PHYSICAL_ADDRESS p, ACPI_SIZE s)
{
if (_io_mem)
Genode::destroy(Acpica::heap(), _io_mem);
_io_mem = nullptr;
Genode::addr_t xsize = _phys - p + _size;
if (!allocate(p, xsize, _ref))
FAIL(0)
return _expand(p, s);
}
Genode::addr_t post_expand(ACPI_PHYSICAL_ADDRESS p, ACPI_SIZE s)
{
if (_io_mem)
Genode::destroy(Acpica::heap(), _io_mem);
ACPI_SIZE xsize = p + s - _phys;
if (!allocate(_phys, xsize, _ref))
FAIL(0)
return _expand(p, s);
}
Genode::addr_t _expand(ACPI_PHYSICAL_ADDRESS const p, ACPI_SIZE const s)
{
/* mark this element as a stale reference */
_ref = ~0U;
/* find new created entry */
Genode::addr_t res = Acpica::Io_mem::apply_u([&] (Acpica::Io_mem &io_mem) {
if (!io_mem.valid() || !io_mem.refs() ||
!io_mem.contains_phys(p, s))
return 0UL;
Genode::Io_mem_dataspace_capability const io_ds = io_mem._io_mem->dataspace();
/* re-attach mem of stale entries partially using this iomem */
unsigned stale_count = 0;
Acpica::Io_mem::apply_to_all([&] (Acpica::Io_mem &io2) {
if (!io2.valid() || !io_mem.contains_phys(io2._phys, 0))
return;
if (io2.refs())
return;
Genode::addr_t off_phys = io2._phys - io_mem._phys;
stale_count ++;
io2._io_mem = io_mem._io_mem;
Genode::addr_t virt = reinterpret_cast<Genode::addr_t>(io2._virt);
Acpica::env().rm().attach_at(io_ds, virt, io2._size, off_phys);
});
/**
* In case the old *this* entry was solely a placeholder for
* other stale entries, the new one *io_mem* becomes just
* a placeholder too -> meaning ref must be increased by 1.
*/
if (io_mem._ref == stale_count)
io_mem._ref ++;
if (io_mem._virt)
FAIL(0UL);
/* attach whole memory */
io_mem._virt = Acpica::env().rm().attach(io_ds);
return io_mem.to_virt(p);
});
/* should never happen */
if (!res)
FAIL(0)
return res;
}
};
Acpica::Io_mem Acpica::Io_mem::_ios[16];
void * AcpiOsMapMemory (ACPI_PHYSICAL_ADDRESS phys, ACPI_SIZE size)
{
Genode::addr_t virt = Acpica::Io_mem::apply_u([&] (Acpica::Io_mem &io_mem) {
if (!io_mem.valid() || !io_mem.refs())
return 0UL;
if (io_mem.contains_phys(phys, size))
/* we have already a mapping in which the request fits */
return io_mem.to_virt(phys);
if (io_mem.contains_phys(phys + 1, 0))
/* phys is within region but end is outside region */
return io_mem.post_expand(phys, size);
if (io_mem.contains_phys(phys + size - 1, 0))
/* phys starts before region and end is within region */
return io_mem.pre_expand(phys, size);
return 0UL;
});
if (virt)
return reinterpret_cast<void *>(virt);
virt= Acpica::Io_mem::insert(phys, size);
if (virt)
return reinterpret_cast<void *>(virt + (phys & 0xfffU));
FAIL(nullptr)
}
void AcpiOsUnmapMemory (void * ptr, ACPI_SIZE size)
{
Genode::uint8_t const * virt = reinterpret_cast<Genode::uint8_t *>(ptr);
if (Acpica::Io_mem::apply_u([&] (Acpica::Io_mem &io_mem) {
if (!io_mem.valid() || !io_mem.contains_virt(virt, size))
return 0;
if (io_mem.refs() && io_mem.ref_dec())
return 1;
io_mem.invalidate(size);
return 1;
}))
return;
FAIL()
}