nova: support creation of vCPU in own PD

With the patch a VMM is not forced anymore to co-located the VMM and VM in same
PD.

Fixes #949
This commit is contained in:
Alexander Boettcher 2013-11-14 10:35:44 +01:00 committed by Norman Feske
parent 1df48b8331
commit 67c1ad4cdd
5 changed files with 83 additions and 31 deletions

View File

@ -52,6 +52,7 @@ namespace Genode {
addr_t _initial_esp;
addr_t _initial_eip;
addr_t _client_exc_pt_sel;
addr_t _client_exc_vcpu;
struct
{
@ -131,6 +132,7 @@ namespace Genode {
*/
addr_t exc_pt_sel() { return _tid.exc_pt_sel; }
addr_t exc_pt_sel_client() { return _client_exc_pt_sel; }
addr_t exc_pt_vcpu() { return _client_exc_vcpu; }
/**
* Set initial stack pointer used by the startup handler
@ -226,6 +228,18 @@ namespace Genode {
* all remotely available portals had been revoked beforehand.
*/
void cleanup_call();
/**
* Open receive window for initial portals for vCPU.
*/
void prepare_vCPU_portals()
{
_client_exc_vcpu = cap_map()->insert(Nova::NUM_INITIAL_VCPU_PT_LOG2);
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(Thread_base::utcb());
utcb->crd_rcv = Nova::Obj_crd(_client_exc_vcpu, Nova::NUM_INITIAL_VCPU_PT_LOG2);
}
};

View File

@ -550,6 +550,7 @@ namespace Nova {
NUM_INITIAL_PT_LOG2 = 5,
NUM_INITIAL_PT = 1UL << NUM_INITIAL_PT_LOG2,
NUM_INITIAL_PT_RESERVED = 2 * NUM_INITIAL_PT,
NUM_INITIAL_VCPU_PT_LOG2 = 8,
};
/**

View File

@ -64,4 +64,27 @@ inline void request_signal_sm_cap(Genode::Native_capability const &cap,
Genode::addr_t sel) {
request_event_portal(cap, sel, ~0UL - 1, 0); }
inline void delegate_vcpu_portals(Genode::Native_capability const &cap,
Genode::addr_t sel)
{
using namespace Nova;
Utcb *utcb = reinterpret_cast<Utcb *>(Genode::Thread_base::myself()->utcb());
/* save original receive window */
Crd orig_crd = utcb->crd_rcv;
utcb->crd_rcv = Obj_crd();
utcb->set_msg_word(0);
uint8_t res = utcb->append_item(Obj_crd(sel, NUM_INITIAL_VCPU_PT_LOG2), 0);
(void)res;
res = call(cap.local_name());
/* restore original receive window */
utcb->crd_rcv = orig_crd;
if (res)
PERR("setting exception portals for vCPU failed %u", res);
}
#endif /* _NOVA__INCLUDE__UTIL_H_ */

View File

@ -194,6 +194,12 @@ void Pager_object::_invoke_handler()
Pager_object *obj;
Utcb *utcb = _check_handler(myself, obj);
/* if protocol is violated ignore request and close receive window */
if (utcb->msg_words() != 2) {
utcb->crd_rcv = Obj_crd();
reply(myself->stack_top());
}
/* send single portal as reply */
addr_t const event = utcb->msg[0];
addr_t const logcount = utcb->msg[1];
@ -325,8 +331,10 @@ static uint8_t create_portal(addr_t pt, addr_t pd, addr_t ec, Mtd mtd,
Pager_object::Pager_object(unsigned long badge, Affinity::Location location)
: Thread_base("pager:", PF_HANDLER_STACK_SIZE),
_badge(reinterpret_cast<unsigned long>(_context->name + 6))
:
Thread_base("pager:", PF_HANDLER_STACK_SIZE),
_badge(reinterpret_cast<unsigned long>(_context->name + 6)),
_client_exc_vcpu(Native_thread::INVALID_INDEX)
{
class Create_exception_pt_failed { };
uint8_t res;
@ -441,6 +449,13 @@ Pager_object::~Pager_object()
cap_map()->remove(_pt_cleanup, 1, false);
cap_map()->remove(exc_pt_sel_client(), NUM_INITIAL_PT_LOG2, false);
if (_client_exc_vcpu == Native_thread::INVALID_INDEX)
return;
/* revoke vCPU exception portals */
revoke(Obj_crd(_client_exc_vcpu, NUM_INITIAL_VCPU_PT_LOG2));
cap_map()->remove(_client_exc_vcpu, NUM_INITIAL_VCPU_PT_LOG2, false);
}

View File

@ -100,38 +100,32 @@ int Platform_thread::start(void *ip, void *sp)
return -5;
}
/*
* For the first thread of a new PD, use the initial stack pointer for
* reporting the thread's UTCB address.
*/
addr_t pd_utcb = Native_config::context_area_virtual_base() +
Native_config::context_area_virtual_size() -
get_page_size();
_sel_exc_base = _pager->exc_pt_sel_client();
addr_t pd_core_sel = Platform_pd::pd_core_sel();
addr_t pd_utcb = 0;
_sel_exc_base = is_vcpu() ? _pager->exc_pt_vcpu() : _pager->exc_pt_sel_client();
addr_t remap_src[] = { _pd->parent_pt_sel() };
addr_t remap_dst[] = { PT_SEL_PARENT };
addr_t pd_sel;
if (!is_vcpu()) {
pd_utcb = Native_config::context_area_virtual_base() +
Native_config::context_area_virtual_size() - get_page_size();
Obj_crd initial_pts(_sel_exc_base, NUM_INITIAL_PT_LOG2);
addr_t remap_src[] = { _pd->parent_pt_sel() };
addr_t remap_dst[] = { PT_SEL_PARENT };
uint8_t res;
/* remap exception portals for first thread */
for (unsigned i = 0; i < sizeof(remap_dst)/sizeof(remap_dst[0]); i++) {
if (map_local((Utcb *)Thread_base::myself()->utcb(),
Obj_crd(remap_src[i], 0),
Obj_crd(_sel_exc_base + remap_dst[i], 0)))
return -6;
/* remap exception portals for first thread */
for (unsigned i = 0; i < sizeof(remap_dst)/sizeof(remap_dst[0]); i++) {
if (map_local((Utcb *)Thread_base::myself()->utcb(),
Obj_crd(remap_src[i], 0),
Obj_crd(_sel_exc_base + remap_dst[i], 0)))
return -6;
}
}
pd_sel = cap_map()->insert();
addr_t pd_sel = cap_map()->insert();
/* create task */
res = create_pd(pd_sel, pd_core_sel, initial_pts);
Obj_crd initial_pts(_sel_exc_base, is_vcpu() ?
NUM_INITIAL_VCPU_PT_LOG2 : NUM_INITIAL_PT_LOG2);
uint8_t res = create_pd(pd_sel, pd_core_sel, initial_pts);
if (res != NOVA_OK) {
PERR("create_pd returned %d", res);
goto cleanup_pd;
@ -238,9 +232,6 @@ Thread_state Platform_thread::state()
void Platform_thread::state(Thread_state s)
{
/* not permitted for main thread */
if (is_main_thread()) throw Cpu_session::State_access_failed();
/* you can do it only once */
if (_sel_exc_base != Native_thread::INVALID_INDEX)
throw Cpu_session::State_access_failed();
@ -251,8 +242,16 @@ void Platform_thread::state(Thread_state s)
* s.is_vcpu If true it will run as vCPU,
* otherwise it will be a thread.
*/
_sel_exc_base = s.sel_exc_base;
if (s.is_vcpu) _features |= VCPU;
if (!is_main_thread())
_sel_exc_base = s.sel_exc_base;
if (!s.is_vcpu)
return;
_features |= VCPU;
if (is_main_thread() && _pager)
_pager->prepare_vCPU_portals();
}