genode/repos/base-hw/src/core/rm_session_support.cc
Martin Stein d31492040c hw: fix race on pager-object dissolve
The HW-kernel, in contrast to other kernels, provides a direct reference
to the pager object with the fault signal that is send to the pager
activation. When accessing this reference directly we may fall into the
time span where the root parent-entrypoint of the faulter has alredy
dissolved the pager object from the pager entrypoint, but not yet
silenced the according signal context.  To avoid this we issue an
additional 'lookup_and_lock' with the received pager object. This isn't
optimal as we don't need the potentially cost-intensive lookup but only the
synchronization.

Fixes #1311.
Fixes #1332.
2014-12-19 13:58:48 +01:00

148 lines
4.0 KiB
C++

/*
* \brief RM- and pager implementations specific for base-hw and core
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2012-02-12
*/
/*
* Copyright (C) 2012-2013 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/pager.h>
/* core includes */
#include <rm_session_component.h>
#include <platform.h>
#include <platform_pd.h>
#include <platform_thread.h>
#include <translation_table.h>
using namespace Genode;
/***************
** Rm_client **
***************/
void Rm_client::unmap(addr_t, addr_t virt_base, size_t size)
{
/* remove mapping from the translation table of the thread that we serve */
Platform_thread * const pt = (Platform_thread *)badge();
if (!pt || !pt->pd()) return;
Lock::Guard guard(*pt->pd()->lock());
Translation_table * const tt = pt->pd()->translation_table();
if (!tt) {
PWRN("failed to get translation table of RM client");
return;
}
tt->remove_translation(virt_base, size,pt->pd()->page_slab());
/* update translation caches of all CPUs */
Kernel::update_pd(pt->pd()->id());
}
/***************************
** Pager_activation_base **
***************************/
int Pager_activation_base::apply_mapping()
{
/* prepare mapping */
Platform_pd * const pd = (Platform_pd*)_fault.pd;
Lock::Guard guard(*pd->lock());
Translation_table * const tt = pd->translation_table();
Page_slab * page_slab = pd->page_slab();
Page_flags const flags =
Page_flags::apply_mapping(_mapping.writable,
_mapping.cacheable,
_mapping.io_mem);
/* insert mapping into translation table */
try {
for (unsigned retry = 0; retry < 2; retry++) {
try {
tt->insert_translation(_mapping.virt_address, _mapping.phys_address,
1 << _mapping.size_log2, flags, page_slab);
return 0;
} catch(Page_slab::Out_of_slabs) {
page_slab->alloc_slab_block();
}
}
} catch(Allocator::Out_of_memory) {
PERR("Translation table needs to much RAM");
} catch(...) {
PERR("Invalid mapping %p -> %p (%zx)", (void*)_mapping.phys_address,
(void*)_mapping.virt_address, 1 << _mapping.size_log2);
}
return -1;
}
void Pager_activation_base::entry()
{
/* get ready to receive faults */
_cap = Native_capability(thread_get_my_native_id(), 0);
_cap_valid.unlock();
while (1)
{
/* receive fault */
Signal s = Signal_receiver::wait_for_signal();
Pager_object * po = static_cast<Pager_object *>(s.context());
/*
* Synchronize access and ensure that the object is still managed
*
* FIXME: The implicit lookup of the oject isn't needed.
*/
unsigned const pon = po->cap().local_name();
Object_pool<Pager_object>::Guard pog(_ep->lookup_and_lock(pon));
if (!pog) {
PWRN("failed to lookup pager object");
continue;
}
/* let pager object go to fault state */
pog->fault_occured(s);
/* fetch fault data */
Platform_thread * const pt = (Platform_thread *)pog->badge();
if (!pt) {
PWRN("failed to get platform thread of faulter");
continue;
}
unsigned const thread_id = pt->id();
typedef Kernel::Thread_reg_id Reg_id;
static addr_t const read_regs[] = {
Reg_id::FAULT_TLB, Reg_id::IP, Reg_id::FAULT_ADDR,
Reg_id::FAULT_WRITES, Reg_id::FAULT_SIGNAL };
enum { READS = sizeof(read_regs)/sizeof(read_regs[0]) };
void * const utcb = Thread_base::myself()->utcb()->base();
memcpy(utcb, read_regs, sizeof(read_regs));
addr_t * const values = (addr_t *)&_fault;
if (Kernel::access_thread_regs(thread_id, READS, 0, values)) {
PWRN("failed to read fault data");
continue;
}
/* try to resolve fault directly via local region managers */
if (pog->pager(*this)) { continue; }
/* apply mapping that was determined by the local region managers */
if (apply_mapping()) {
PWRN("failed to apply mapping");
continue;
}
/* let pager object go back to no-fault state */
pog->fault_resolved();
}
}