diff --git a/repos/base-nova/include/nova/syscall-generic.h b/repos/base-nova/include/nova/syscall-generic.h index c8afbc2670..cff0532830 100644 --- a/repos/base-nova/include/nova/syscall-generic.h +++ b/repos/base-nova/include/nova/syscall-generic.h @@ -3,11 +3,12 @@ * \author Norman Feske * \author Sebastian Sumpf * \author Alexander Boettcher + * \author Benjamin Lamowski * \date 2009-12-27 */ /* - * Copyright (c) 2009-2022 Genode Labs + * Copyright (c) 2009-2023 Genode Labs * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -284,6 +285,8 @@ namespace Nova { EC_RESCHEDULE = 3U, EC_MIGRATE = 4U, EC_TIME = 5U, + EC_GET_VCPU_STATE = 6U, + EC_SET_VCPU_STATE = 7U, }; enum Sc_op { @@ -665,6 +668,7 @@ namespace Nova { #endif } gdtr, idtr; unsigned long long tsc_val, tsc_off, tsc_aux; + unsigned long long exit_reason; uint8_t fpu[512]; } __attribute__((packed)); mword_t mr[(4096 - 4 * sizeof(mword_t)) / sizeof(mword_t)]; diff --git a/repos/base-nova/include/spec/64bit/nova/syscalls.h b/repos/base-nova/include/spec/64bit/nova/syscalls.h index 0491379fa9..f116d73585 100644 --- a/repos/base-nova/include/spec/64bit/nova/syscalls.h +++ b/repos/base-nova/include/spec/64bit/nova/syscalls.h @@ -3,11 +3,12 @@ * \author Norman Feske * \author Sebastian Sumpf * \author Alexander Boettcher + * \author Benjamin Lamowski * \date 2012-06-06 */ /* - * Copyright (c) 2012 Genode Labs + * Copyright (c) 2012-2023 Genode Labs * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -362,7 +363,6 @@ namespace Nova { return syscall_2(NOVA_MISC, 2, sm_auth_acpi, sleep_state_a, sleep_state_b); } - ALWAYS_INLINE inline uint8_t sm_ctrl(mword_t sm, Sem_op op, unsigned long long timeout = 0) { diff --git a/repos/base-nova/ports/nova.hash b/repos/base-nova/ports/nova.hash index 4777898028..05e7c3d6a6 100644 --- a/repos/base-nova/ports/nova.hash +++ b/repos/base-nova/ports/nova.hash @@ -1 +1 @@ -a9f11fdc132f027ed1d58da86fe39679da98699c +3f6464b9126ffc77fdfd4b670ed7b4d3d3d0aff8 diff --git a/repos/base-nova/ports/nova.port b/repos/base-nova/ports/nova.port index 01ccd14379..a73583628e 100644 --- a/repos/base-nova/ports/nova.port +++ b/repos/base-nova/ports/nova.port @@ -4,7 +4,7 @@ DOWNLOADS := nova.git # r10 branch URL(nova) := https://github.com/alex-ab/NOVA.git -REV(nova) := 1dff7b1311016b111ce71365ffe7ba625fa4708c +REV(nova) := 89f714dcb32c7f4e97d29a8651e55862b8362df1 DIR(nova) := src/kernel/nova PATCHES := $(sort $(wildcard $(REP_DIR)/patches/*.patch)) diff --git a/repos/base-nova/src/lib/base/vm.cc b/repos/base-nova/src/lib/base/vm.cc index 9e0f7669c0..8d1709e78b 100644 --- a/repos/base-nova/src/lib/base/vm.cc +++ b/repos/base-nova/src/lib/base/vm.cc @@ -2,11 +2,12 @@ * \brief NOVA-specific VM-connection implementation * \author Alexander Boettcher * \author Christian Helmuth + * \author Benjamin Lamowski * \date 2018-08-27 */ /* - * Copyright (C) 2018-2021 Genode Labs GmbH + * Copyright (C) 2018-2023 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. @@ -16,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +35,7 @@ using namespace Genode; using Exit_config = Vm_connection::Exit_config; +using Call_with_state = Vm_connection::Call_with_state; /****************************** @@ -43,6 +46,8 @@ struct Nova_vcpu : Rpc_client, Noncopyable { private: + enum { VM_EXIT_STARTUP = 0xfe, VM_EXIT_RECALL = 0xff }; + typedef Id_space Vcpu_space; static Vcpu_space &_vcpu_space() @@ -59,17 +64,12 @@ struct Nova_vcpu : Rpc_client, Noncopyable Allocator &_alloc; void *_ep_handler { nullptr }; void *_dispatching { nullptr }; - bool _block { true }; + bool _resume { false }; + bool _last_resume { true }; Vcpu_state _vcpu_state __attribute__((aligned(0x10))) { }; - enum Remote_state_requested { - NONE = 0, - PAUSE = 1, - RUN = 2 - } _remote { NONE }; - - inline void _read_nova_state(Nova::Utcb &, unsigned exit_reason); + inline void _read_nova_state(Nova::Utcb &); inline void _write_nova_state(Nova::Utcb &); @@ -96,7 +96,7 @@ struct Nova_vcpu : Rpc_client, Noncopyable uint32_t value() const { return _value; } }; - bool _handle_exit(Nova::Utcb &, uint16_t exit_reason); + void _handle_exit(Nova::Utcb &); __attribute__((regparm(1))) static void _exit_entry(addr_t badge); @@ -146,7 +146,6 @@ struct Nova_vcpu : Rpc_client, Noncopyable uint16_t vcpu_id, uint16_t exit_reason, Nova::Mtd mtd); - /* * Noncopyable */ @@ -158,181 +157,182 @@ struct Nova_vcpu : Rpc_client, Noncopyable Nova_vcpu(Env &env, Vm_connection &vm, Allocator &alloc, Vcpu_handler_base &handler, Exit_config const &exit_config); - void run(); + void startup() + { + call(); + } - void pause(); - - Vcpu_state & state() { return _vcpu_state; } + void with_state(Call_with_state &cw); }; -void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb, unsigned exit_reason) +void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb) { typedef Genode::Vcpu_state::Segment Segment; typedef Genode::Vcpu_state::Range Range; - state().discharge(); - state().exit_reason = exit_reason; + _vcpu_state.discharge(); + _vcpu_state.exit_reason = static_cast(utcb.exit_reason); if (utcb.mtd & Nova::Mtd::FPU) { - state().fpu.charge([&] (Vcpu_state::Fpu::State &fpu) { + _vcpu_state.fpu.charge([&] (Vcpu_state::Fpu::State &fpu) { memcpy(&fpu, utcb.fpu, sizeof(fpu)); }); } if (utcb.mtd & Nova::Mtd::ACDB) { - state().ax.charge(utcb.ax); - state().cx.charge(utcb.cx); - state().dx.charge(utcb.dx); - state().bx.charge(utcb.bx); + _vcpu_state.ax.charge(utcb.ax); + _vcpu_state.cx.charge(utcb.cx); + _vcpu_state.dx.charge(utcb.dx); + _vcpu_state.bx.charge(utcb.bx); } if (utcb.mtd & Nova::Mtd::EBSD) { - state().di.charge(utcb.di); - state().si.charge(utcb.si); - state().bp.charge(utcb.bp); + _vcpu_state.di.charge(utcb.di); + _vcpu_state.si.charge(utcb.si); + _vcpu_state.bp.charge(utcb.bp); } - if (utcb.mtd & Nova::Mtd::EFL) state().flags.charge(utcb.flags); - if (utcb.mtd & Nova::Mtd::ESP) state().sp.charge(utcb.sp); - if (utcb.mtd & Nova::Mtd::DR) state().dr7.charge(utcb.dr7); + if (utcb.mtd & Nova::Mtd::EFL) _vcpu_state.flags.charge(utcb.flags); + if (utcb.mtd & Nova::Mtd::ESP) _vcpu_state.sp.charge(utcb.sp); + if (utcb.mtd & Nova::Mtd::DR) _vcpu_state.dr7.charge(utcb.dr7); if (utcb.mtd & Nova::Mtd::EIP) { - state().ip.charge(utcb.ip); - state().ip_len.charge(utcb.instr_len); + _vcpu_state.ip.charge(utcb.ip); + _vcpu_state.ip_len.charge(utcb.instr_len); } if (utcb.mtd & Nova::Mtd::R8_R15) { - state(). r8.charge(utcb.read_r8()); - state(). r9.charge(utcb.read_r9()); - state().r10.charge(utcb.read_r10()); - state().r11.charge(utcb.read_r11()); - state().r12.charge(utcb.read_r12()); - state().r13.charge(utcb.read_r13()); - state().r14.charge(utcb.read_r14()); - state().r15.charge(utcb.read_r15()); + _vcpu_state. r8.charge(utcb.read_r8()); + _vcpu_state. r9.charge(utcb.read_r9()); + _vcpu_state.r10.charge(utcb.read_r10()); + _vcpu_state.r11.charge(utcb.read_r11()); + _vcpu_state.r12.charge(utcb.read_r12()); + _vcpu_state.r13.charge(utcb.read_r13()); + _vcpu_state.r14.charge(utcb.read_r14()); + _vcpu_state.r15.charge(utcb.read_r15()); } if (utcb.mtd & Nova::Mtd::CR) { - state().cr0.charge(utcb.cr0); - state().cr2.charge(utcb.cr2); - state().cr3.charge(utcb.cr3); - state().cr4.charge(utcb.cr4); + _vcpu_state.cr0.charge(utcb.cr0); + _vcpu_state.cr2.charge(utcb.cr2); + _vcpu_state.cr3.charge(utcb.cr3); + _vcpu_state.cr4.charge(utcb.cr4); } if (utcb.mtd & Nova::Mtd::CSSS) { - state().cs.charge(Segment { .sel = utcb.cs.sel, + _vcpu_state.cs.charge(Segment { .sel = utcb.cs.sel, .ar = utcb.cs.ar, .limit = utcb.cs.limit, .base = utcb.cs.base }); - state().ss.charge(Segment { .sel = utcb.ss.sel, + _vcpu_state.ss.charge(Segment { .sel = utcb.ss.sel, .ar = utcb.ss.ar, .limit = utcb.ss.limit, .base = utcb.ss.base }); } if (utcb.mtd & Nova::Mtd::ESDS) { - state().es.charge(Segment { .sel = utcb.es.sel, + _vcpu_state.es.charge(Segment { .sel = utcb.es.sel, .ar = utcb.es.ar, .limit = utcb.es.limit, .base = utcb.es.base }); - state().ds.charge(Segment { .sel = utcb.ds.sel, + _vcpu_state.ds.charge(Segment { .sel = utcb.ds.sel, .ar = utcb.ds.ar, .limit = utcb.ds.limit, .base = utcb.ds.base }); } if (utcb.mtd & Nova::Mtd::FSGS) { - state().fs.charge(Segment { .sel = utcb.fs.sel, + _vcpu_state.fs.charge(Segment { .sel = utcb.fs.sel, .ar = utcb.fs.ar, .limit = utcb.fs.limit, .base = utcb.fs.base }); - state().gs.charge(Segment { .sel = utcb.gs.sel, + _vcpu_state.gs.charge(Segment { .sel = utcb.gs.sel, .ar = utcb.gs.ar, .limit = utcb.gs.limit, .base = utcb.gs.base }); } if (utcb.mtd & Nova::Mtd::TR) { - state().tr.charge(Segment { .sel = utcb.tr.sel, + _vcpu_state.tr.charge(Segment { .sel = utcb.tr.sel, .ar = utcb.tr.ar, .limit = utcb.tr.limit, .base = utcb.tr.base }); } if (utcb.mtd & Nova::Mtd::LDTR) { - state().ldtr.charge(Segment { .sel = utcb.ldtr.sel, + _vcpu_state.ldtr.charge(Segment { .sel = utcb.ldtr.sel, .ar = utcb.ldtr.ar, .limit = utcb.ldtr.limit, .base = utcb.ldtr.base }); } if (utcb.mtd & Nova::Mtd::GDTR) { - state().gdtr.charge(Range { .limit = utcb.gdtr.limit, + _vcpu_state.gdtr.charge(Range { .limit = utcb.gdtr.limit, .base = utcb.gdtr.base }); } if (utcb.mtd & Nova::Mtd::IDTR) { - state().idtr.charge(Range { .limit = utcb.idtr.limit, + _vcpu_state.idtr.charge(Range { .limit = utcb.idtr.limit, .base = utcb.idtr.base }); } if (utcb.mtd & Nova::Mtd::SYS) { - state().sysenter_cs.charge(utcb.sysenter_cs); - state().sysenter_sp.charge(utcb.sysenter_sp); - state().sysenter_ip.charge(utcb.sysenter_ip); + _vcpu_state.sysenter_cs.charge(utcb.sysenter_cs); + _vcpu_state.sysenter_sp.charge(utcb.sysenter_sp); + _vcpu_state.sysenter_ip.charge(utcb.sysenter_ip); } if (utcb.mtd & Nova::Mtd::QUAL) { - state().qual_primary.charge(utcb.qual[0]); - state().qual_secondary.charge(utcb.qual[1]); + _vcpu_state.qual_primary.charge(utcb.qual[0]); + _vcpu_state.qual_secondary.charge(utcb.qual[1]); } if (utcb.mtd & Nova::Mtd::CTRL) { - state().ctrl_primary.charge(utcb.ctrl[0]); - state().ctrl_secondary.charge(utcb.ctrl[1]); + _vcpu_state.ctrl_primary.charge(utcb.ctrl[0]); + _vcpu_state.ctrl_secondary.charge(utcb.ctrl[1]); } if (utcb.mtd & Nova::Mtd::INJ) { - state().inj_info.charge(utcb.inj_info); - state().inj_error.charge(utcb.inj_error); + _vcpu_state.inj_info.charge(utcb.inj_info); + _vcpu_state.inj_error.charge(utcb.inj_error); } if (utcb.mtd & Nova::Mtd::STA) { - state().intr_state.charge(utcb.intr_state); - state().actv_state.charge(utcb.actv_state); + _vcpu_state.intr_state.charge(utcb.intr_state); + _vcpu_state.actv_state.charge(utcb.actv_state); } if (utcb.mtd & Nova::Mtd::TSC) { - state().tsc.charge(utcb.tsc_val); - state().tsc_offset.charge(utcb.tsc_off); + _vcpu_state.tsc.charge(utcb.tsc_val); + _vcpu_state.tsc_offset.charge(utcb.tsc_off); } if (utcb.mtd & Nova::Mtd::TSC_AUX) { - state().tsc_aux.charge(utcb.tsc_aux); + _vcpu_state.tsc_aux.charge(utcb.tsc_aux); } if (utcb.mtd & Nova::Mtd::EFER) { - state().efer.charge(utcb.read_efer()); + _vcpu_state.efer.charge(utcb.read_efer()); } if (utcb.mtd & Nova::Mtd::PDPTE) { - state().pdpte_0.charge(utcb.pdpte[0]); - state().pdpte_1.charge(utcb.pdpte[1]); - state().pdpte_2.charge(utcb.pdpte[2]); - state().pdpte_3.charge(utcb.pdpte[3]); + _vcpu_state.pdpte_0.charge(utcb.pdpte[0]); + _vcpu_state.pdpte_1.charge(utcb.pdpte[1]); + _vcpu_state.pdpte_2.charge(utcb.pdpte[2]); + _vcpu_state.pdpte_3.charge(utcb.pdpte[3]); } if (utcb.mtd & Nova::Mtd::SYSCALL_SWAPGS) { - state().star.charge(utcb.read_star()); - state().lstar.charge(utcb.read_lstar()); - state().cstar.charge(utcb.read_cstar()); - state().fmask.charge(utcb.read_fmask()); - state().kernel_gs_base.charge(utcb.read_kernel_gs_base()); + _vcpu_state.star.charge(utcb.read_star()); + _vcpu_state.lstar.charge(utcb.read_lstar()); + _vcpu_state.cstar.charge(utcb.read_cstar()); + _vcpu_state.fmask.charge(utcb.read_fmask()); + _vcpu_state.kernel_gs_base.charge(utcb.read_kernel_gs_base()); } if (utcb.mtd & Nova::Mtd::TPR) { - state().tpr.charge(utcb.read_tpr()); - state().tpr_threshold.charge(utcb.read_tpr_threshold()); + _vcpu_state.tpr.charge(utcb.read_tpr()); + _vcpu_state.tpr_threshold.charge(utcb.read_tpr_threshold()); } } @@ -342,291 +342,234 @@ void Nova_vcpu::_write_nova_state(Nova::Utcb &utcb) utcb.items = 0; utcb.mtd = 0; - if (state().ax.charged() || state().cx.charged() || - state().dx.charged() || state().bx.charged()) { + if (_vcpu_state.ax.charged() || _vcpu_state.cx.charged() || + _vcpu_state.dx.charged() || _vcpu_state.bx.charged()) { utcb.mtd |= Nova::Mtd::ACDB; - utcb.ax = state().ax.value(); - utcb.cx = state().cx.value(); - utcb.dx = state().dx.value(); - utcb.bx = state().bx.value(); + utcb.ax = _vcpu_state.ax.value(); + utcb.cx = _vcpu_state.cx.value(); + utcb.dx = _vcpu_state.dx.value(); + utcb.bx = _vcpu_state.bx.value(); } - if (state().bp.charged() || state().di.charged() || state().si.charged()) { + if (_vcpu_state.bp.charged() || _vcpu_state.di.charged() || _vcpu_state.si.charged()) { utcb.mtd |= Nova::Mtd::EBSD; - utcb.di = state().di.value(); - utcb.si = state().si.value(); - utcb.bp = state().bp.value(); + utcb.di = _vcpu_state.di.value(); + utcb.si = _vcpu_state.si.value(); + utcb.bp = _vcpu_state.bp.value(); } - if (state().flags.charged()) { + if (_vcpu_state.flags.charged()) { utcb.mtd |= Nova::Mtd::EFL; - utcb.flags = state().flags.value(); + utcb.flags = _vcpu_state.flags.value(); } - if (state().sp.charged()) { + if (_vcpu_state.sp.charged()) { utcb.mtd |= Nova::Mtd::ESP; - utcb.sp = state().sp.value(); + utcb.sp = _vcpu_state.sp.value(); } - if (state().ip.charged()) { + if (_vcpu_state.ip.charged()) { utcb.mtd |= Nova::Mtd::EIP; - utcb.ip = state().ip.value(); - utcb.instr_len = state().ip_len.value(); + utcb.ip = _vcpu_state.ip.value(); + utcb.instr_len = _vcpu_state.ip_len.value(); } - if (state().dr7.charged()) { + if (_vcpu_state.dr7.charged()) { utcb.mtd |= Nova::Mtd::DR; - utcb.dr7 = state().dr7.value(); + utcb.dr7 = _vcpu_state.dr7.value(); } - if (state().r8 .charged() || state().r9 .charged() || - state().r10.charged() || state().r11.charged() || - state().r12.charged() || state().r13.charged() || - state().r14.charged() || state().r15.charged()) { + if (_vcpu_state.r8 .charged() || _vcpu_state.r9 .charged() || + _vcpu_state.r10.charged() || _vcpu_state.r11.charged() || + _vcpu_state.r12.charged() || _vcpu_state.r13.charged() || + _vcpu_state.r14.charged() || _vcpu_state.r15.charged()) { utcb.mtd |= Nova::Mtd::R8_R15; - utcb.write_r8 (state().r8.value()); - utcb.write_r9 (state().r9.value()); - utcb.write_r10(state().r10.value()); - utcb.write_r11(state().r11.value()); - utcb.write_r12(state().r12.value()); - utcb.write_r13(state().r13.value()); - utcb.write_r14(state().r14.value()); - utcb.write_r15(state().r15.value()); + utcb.write_r8 (_vcpu_state.r8.value()); + utcb.write_r9 (_vcpu_state.r9.value()); + utcb.write_r10(_vcpu_state.r10.value()); + utcb.write_r11(_vcpu_state.r11.value()); + utcb.write_r12(_vcpu_state.r12.value()); + utcb.write_r13(_vcpu_state.r13.value()); + utcb.write_r14(_vcpu_state.r14.value()); + utcb.write_r15(_vcpu_state.r15.value()); } - if (state().cr0.charged() || state().cr2.charged() || - state().cr3.charged() || state().cr4.charged()) { + if (_vcpu_state.cr0.charged() || _vcpu_state.cr2.charged() || + _vcpu_state.cr3.charged() || _vcpu_state.cr4.charged()) { utcb.mtd |= Nova::Mtd::CR; - utcb.cr0 = state().cr0.value(); - utcb.cr2 = state().cr2.value(); - utcb.cr3 = state().cr3.value(); - utcb.cr4 = state().cr4.value(); + utcb.cr0 = _vcpu_state.cr0.value(); + utcb.cr2 = _vcpu_state.cr2.value(); + utcb.cr3 = _vcpu_state.cr3.value(); + utcb.cr4 = _vcpu_state.cr4.value(); } - if (state().cs.charged() || state().ss.charged()) { + if (_vcpu_state.cs.charged() || _vcpu_state.ss.charged()) { utcb.mtd |= Nova::Mtd::CSSS; - utcb.cs.sel = state().cs.value().sel; - utcb.cs.ar = state().cs.value().ar; - utcb.cs.limit = state().cs.value().limit; - utcb.cs.base = state().cs.value().base; + utcb.cs.sel = _vcpu_state.cs.value().sel; + utcb.cs.ar = _vcpu_state.cs.value().ar; + utcb.cs.limit = _vcpu_state.cs.value().limit; + utcb.cs.base = _vcpu_state.cs.value().base; - utcb.ss.sel = state().ss.value().sel; - utcb.ss.ar = state().ss.value().ar; - utcb.ss.limit = state().ss.value().limit; - utcb.ss.base = state().ss.value().base; + utcb.ss.sel = _vcpu_state.ss.value().sel; + utcb.ss.ar = _vcpu_state.ss.value().ar; + utcb.ss.limit = _vcpu_state.ss.value().limit; + utcb.ss.base = _vcpu_state.ss.value().base; } - if (state().es.charged() || state().ds.charged()) { + if (_vcpu_state.es.charged() || _vcpu_state.ds.charged()) { utcb.mtd |= Nova::Mtd::ESDS; - utcb.es.sel = state().es.value().sel; - utcb.es.ar = state().es.value().ar; - utcb.es.limit = state().es.value().limit; - utcb.es.base = state().es.value().base; + utcb.es.sel = _vcpu_state.es.value().sel; + utcb.es.ar = _vcpu_state.es.value().ar; + utcb.es.limit = _vcpu_state.es.value().limit; + utcb.es.base = _vcpu_state.es.value().base; - utcb.ds.sel = state().ds.value().sel; - utcb.ds.ar = state().ds.value().ar; - utcb.ds.limit = state().ds.value().limit; - utcb.ds.base = state().ds.value().base; + utcb.ds.sel = _vcpu_state.ds.value().sel; + utcb.ds.ar = _vcpu_state.ds.value().ar; + utcb.ds.limit = _vcpu_state.ds.value().limit; + utcb.ds.base = _vcpu_state.ds.value().base; } - if (state().fs.charged() || state().gs.charged()) { + if (_vcpu_state.fs.charged() || _vcpu_state.gs.charged()) { utcb.mtd |= Nova::Mtd::FSGS; - utcb.fs.sel = state().fs.value().sel; - utcb.fs.ar = state().fs.value().ar; - utcb.fs.limit = state().fs.value().limit; - utcb.fs.base = state().fs.value().base; + utcb.fs.sel = _vcpu_state.fs.value().sel; + utcb.fs.ar = _vcpu_state.fs.value().ar; + utcb.fs.limit = _vcpu_state.fs.value().limit; + utcb.fs.base = _vcpu_state.fs.value().base; - utcb.gs.sel = state().gs.value().sel; - utcb.gs.ar = state().gs.value().ar; - utcb.gs.limit = state().gs.value().limit; - utcb.gs.base = state().gs.value().base; + utcb.gs.sel = _vcpu_state.gs.value().sel; + utcb.gs.ar = _vcpu_state.gs.value().ar; + utcb.gs.limit = _vcpu_state.gs.value().limit; + utcb.gs.base = _vcpu_state.gs.value().base; } - if (state().tr.charged()) { + if (_vcpu_state.tr.charged()) { utcb.mtd |= Nova::Mtd::TR; - utcb.tr.sel = state().tr.value().sel; - utcb.tr.ar = state().tr.value().ar; - utcb.tr.limit = state().tr.value().limit; - utcb.tr.base = state().tr.value().base; + utcb.tr.sel = _vcpu_state.tr.value().sel; + utcb.tr.ar = _vcpu_state.tr.value().ar; + utcb.tr.limit = _vcpu_state.tr.value().limit; + utcb.tr.base = _vcpu_state.tr.value().base; } - if (state().ldtr.charged()) { + if (_vcpu_state.ldtr.charged()) { utcb.mtd |= Nova::Mtd::LDTR; - utcb.ldtr.sel = state().ldtr.value().sel; - utcb.ldtr.ar = state().ldtr.value().ar; - utcb.ldtr.limit = state().ldtr.value().limit; - utcb.ldtr.base = state().ldtr.value().base; + utcb.ldtr.sel = _vcpu_state.ldtr.value().sel; + utcb.ldtr.ar = _vcpu_state.ldtr.value().ar; + utcb.ldtr.limit = _vcpu_state.ldtr.value().limit; + utcb.ldtr.base = _vcpu_state.ldtr.value().base; } - if (state().gdtr.charged()) { + if (_vcpu_state.gdtr.charged()) { utcb.mtd |= Nova::Mtd::GDTR; - utcb.gdtr.limit = state().gdtr.value().limit; - utcb.gdtr.base = state().gdtr.value().base; + utcb.gdtr.limit = _vcpu_state.gdtr.value().limit; + utcb.gdtr.base = _vcpu_state.gdtr.value().base; } - if (state().idtr.charged()) { + if (_vcpu_state.idtr.charged()) { utcb.mtd |= Nova::Mtd::IDTR; - utcb.idtr.limit = state().idtr.value().limit; - utcb.idtr.base = state().idtr.value().base; + utcb.idtr.limit = _vcpu_state.idtr.value().limit; + utcb.idtr.base = _vcpu_state.idtr.value().base; } - if (state().sysenter_cs.charged() || state().sysenter_sp.charged() || - state().sysenter_ip.charged()) { + if (_vcpu_state.sysenter_cs.charged() || _vcpu_state.sysenter_sp.charged() || + _vcpu_state.sysenter_ip.charged()) { utcb.mtd |= Nova::Mtd::SYS; - utcb.sysenter_cs = state().sysenter_cs.value(); - utcb.sysenter_sp = state().sysenter_sp.value(); - utcb.sysenter_ip = state().sysenter_ip.value(); + utcb.sysenter_cs = _vcpu_state.sysenter_cs.value(); + utcb.sysenter_sp = _vcpu_state.sysenter_sp.value(); + utcb.sysenter_ip = _vcpu_state.sysenter_ip.value(); } - if (state().ctrl_primary.charged() || state().ctrl_secondary.charged()) { + if (_vcpu_state.ctrl_primary.charged() || _vcpu_state.ctrl_secondary.charged()) { utcb.mtd |= Nova::Mtd::CTRL; - utcb.ctrl[0] = state().ctrl_primary.value(); - utcb.ctrl[1] = state().ctrl_secondary.value(); + utcb.ctrl[0] = _vcpu_state.ctrl_primary.value(); + utcb.ctrl[1] = _vcpu_state.ctrl_secondary.value(); } - if (state().inj_info.charged() || state().inj_error.charged()) { + if (_vcpu_state.inj_info.charged() || _vcpu_state.inj_error.charged()) { utcb.mtd |= Nova::Mtd::INJ; - utcb.inj_info = state().inj_info.value(); - utcb.inj_error = state().inj_error.value(); + utcb.inj_info = _vcpu_state.inj_info.value(); + utcb.inj_error = _vcpu_state.inj_error.value(); } - if (state().intr_state.charged() || state().actv_state.charged()) { + if (_vcpu_state.intr_state.charged() || _vcpu_state.actv_state.charged()) { utcb.mtd |= Nova::Mtd::STA; - utcb.intr_state = state().intr_state.value(); - utcb.actv_state = state().actv_state.value(); + utcb.intr_state = _vcpu_state.intr_state.value(); + utcb.actv_state = _vcpu_state.actv_state.value(); } - if (state().tsc.charged() || state().tsc_offset.charged()) { + if (_vcpu_state.tsc.charged() || _vcpu_state.tsc_offset.charged()) { utcb.mtd |= Nova::Mtd::TSC; - utcb.tsc_val = state().tsc.value(); - utcb.tsc_off = state().tsc_offset.value(); + utcb.tsc_val = _vcpu_state.tsc.value(); + utcb.tsc_off = _vcpu_state.tsc_offset.value(); } - if (state().tsc_aux.charged()) { + if (_vcpu_state.tsc_aux.charged()) { utcb.mtd |= Nova::Mtd::TSC_AUX; - utcb.tsc_aux = state().tsc_aux.value(); + utcb.tsc_aux = _vcpu_state.tsc_aux.value(); } - if (state().efer.charged()) { + if (_vcpu_state.efer.charged()) { utcb.mtd |= Nova::Mtd::EFER; - utcb.write_efer(state().efer.value()); + utcb.write_efer(_vcpu_state.efer.value()); } - if (state().pdpte_0.charged() || state().pdpte_1.charged() || - state().pdpte_2.charged() || state().pdpte_3.charged()) { + if (_vcpu_state.pdpte_0.charged() || _vcpu_state.pdpte_1.charged() || + _vcpu_state.pdpte_2.charged() || _vcpu_state.pdpte_3.charged()) { utcb.mtd |= Nova::Mtd::PDPTE; - utcb.pdpte[0] = (Nova::mword_t)state().pdpte_0.value(); - utcb.pdpte[1] = (Nova::mword_t)state().pdpte_1.value(); - utcb.pdpte[2] = (Nova::mword_t)state().pdpte_2.value(); - utcb.pdpte[3] = (Nova::mword_t)state().pdpte_3.value(); + utcb.pdpte[0] = (Nova::mword_t)_vcpu_state.pdpte_0.value(); + utcb.pdpte[1] = (Nova::mword_t)_vcpu_state.pdpte_1.value(); + utcb.pdpte[2] = (Nova::mword_t)_vcpu_state.pdpte_2.value(); + utcb.pdpte[3] = (Nova::mword_t)_vcpu_state.pdpte_3.value(); } - if (state().star.charged() || state().lstar.charged() || - state().cstar.charged() || state().fmask.charged() || - state().kernel_gs_base.charged()) { + if (_vcpu_state.star.charged() || _vcpu_state.lstar.charged() || + _vcpu_state.cstar.charged() || _vcpu_state.fmask.charged() || + _vcpu_state.kernel_gs_base.charged()) { utcb.mtd |= Nova::Mtd::SYSCALL_SWAPGS; - utcb.write_star (state().star.value()); - utcb.write_lstar(state().lstar.value()); - utcb.write_cstar(state().cstar.value()); - utcb.write_fmask(state().fmask.value()); - utcb.write_kernel_gs_base(state().kernel_gs_base.value()); + utcb.write_star (_vcpu_state.star.value()); + utcb.write_lstar(_vcpu_state.lstar.value()); + utcb.write_cstar(_vcpu_state.cstar.value()); + utcb.write_fmask(_vcpu_state.fmask.value()); + utcb.write_kernel_gs_base(_vcpu_state.kernel_gs_base.value()); } - if (state().tpr.charged() || state().tpr_threshold.charged()) { + if (_vcpu_state.tpr.charged() || _vcpu_state.tpr_threshold.charged()) { utcb.mtd |= Nova::Mtd::TPR; - utcb.write_tpr(state().tpr.value()); - utcb.write_tpr_threshold(state().tpr_threshold.value()); + utcb.write_tpr(_vcpu_state.tpr.value()); + utcb.write_tpr_threshold(_vcpu_state.tpr_threshold.value()); } - if (state().fpu.charged()) { + if (_vcpu_state.fpu.charged()) { utcb.mtd |= Nova::Mtd::FPU; - state().fpu.with_state([&] (Vcpu_state::Fpu::State const &fpu) { + _vcpu_state.fpu.with_state([&] (Vcpu_state::Fpu::State const &fpu) { memcpy(utcb.fpu, &fpu, sizeof(fpu)); }); } } -void Nova_vcpu::run() -{ - if (!_ep_handler) { - /* not started yet - trigger startup of native vCPU */ - call(); - return; - } - - Thread * const current = Thread::myself(); - - if (_dispatching == current) { - _block = false; - return; - } - - if ((_ep_handler == current) && !_block) - return; - - if (_ep_handler != current) - _remote = RUN; - - Nova::ec_ctrl(Nova::EC_RECALL, _ec_sel()); - Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP); -} - - /* * Do not touch the UTCB before _read_nova_state() and after * _write_nova_state(), particularly not by logging diagnostics. */ -bool Nova_vcpu::_handle_exit(Nova::Utcb &utcb, uint16_t exit_reason) +void Nova_vcpu::_handle_exit(Nova::Utcb &utcb) { - /* reset blocking state */ - bool const previous_blocked = _block; - _block = true; - - /* NOVA specific exit reasons */ - enum { VM_EXIT_STARTUP = 0xfe, VM_EXIT_RECALL = 0xff }; - - if (exit_reason == VM_EXIT_STARTUP) - _ep_handler = Thread::myself(); - - /* transform state from NOVA to Genode */ - if (exit_reason != VM_EXIT_RECALL || !previous_blocked) - _read_nova_state(utcb, exit_reason); - - if (exit_reason == VM_EXIT_RECALL) { - if (previous_blocked) - state().exit_reason = exit_reason; - - /* consume potential multiple sem ups */ - Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP); - Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_DOWNZERO); - - if (_remote == PAUSE) { - _remote = NONE; - } else { - if (_remote == RUN) { - _remote = NONE; - if (!previous_blocked) { - /* still running - reply without state transfer */ - _block = false; - utcb.items = 0; - utcb.mtd = 0; - return false; - } - } - - if (previous_blocked) { - /* resume vCPU - with vCPU state update */ - _block = false; - _write_nova_state(utcb); - return false; - } - } + if (utcb.exit_reason == VM_EXIT_RECALL) { + /* + * A recall exit is only requested from an asynchronous Signal to the + * vCPU Handler. In that case, VM_EXIT_RECALL has already been processed + * asynchronously by getting and setting the state via system calls and + * the regular exit does not need to be processed. + */ + utcb.mtd = 0; + utcb.items = 0; + return; } + _read_nova_state(utcb); + try { _dispatching = Thread::myself(); /* call dispatch handler */ @@ -637,36 +580,92 @@ bool Nova_vcpu::_handle_exit(Nova::Utcb &utcb, uint16_t exit_reason) throw; } - if (_block) { - /* block vCPU in kernel - no vCPU state update */ - utcb.items = 0; - utcb.mtd = 0; - return true; - } - /* reply to NOVA and transfer vCPU state */ _write_nova_state(utcb); - return false; } +void Nova_vcpu::with_state(Call_with_state &cw) +{ + Thread *myself = Thread::myself(); + bool remote = (_dispatching != myself); + Nova::Utcb &utcb = *reinterpret_cast(myself->utcb()); + + if (remote) { + if (Thread::myself() != _ep_handler) { + error("vCPU state requested outside of vcpu_handler EP"); + sleep_forever(); + }; + + Exit_config config { }; + Nova::Mtd mtd = _portal_mtd(0, config); + + uint8_t res = Nova::ec_ctrl(Nova::EC_GET_VCPU_STATE, _ec_sel(), mtd.value()); + + if (res != Nova::NOVA_OK) { + error("Getting vCPU state failed with: ", res); + sleep_forever(); + }; + + _read_nova_state(utcb); + } + + _resume = cw.call_with_state(_vcpu_state); + + if (remote) { + _write_nova_state(utcb); + /* + * A recall is needed + * a) when the vCPU should be stopped or + * b) when the vCPU should be resumed from a stopped state. + */ + bool recall = !(_resume && _last_resume); + + uint8_t res = Nova::ec_ctrl(Nova::EC_SET_VCPU_STATE, _ec_sel(), recall); + + if (res != Nova::NOVA_OK) { + error("Setting vCPU state failed with: ", res); + sleep_forever(); + }; + + /* + * Resume the vCPU and indicate to the next exit if state + * needs to be synced or not. + */ + if (_resume) + Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP); + } +} + + +template +static void nova_reply(Thread &myself, Nova::Utcb &utcb, ARGS &&... args) +{ + Receive_window &rcv_window = myself.native_thread().server_rcv_window; + + /* reset receive window to values expected by RPC server code */ + rcv_window.prepare_rcv_window(utcb); + + Nova::reply(myself.stack_top(), args...); +} + void Nova_vcpu::_exit_entry(addr_t badge) { Thread &myself = *Thread::myself(); Nova::Utcb &utcb = *reinterpret_cast(myself.utcb()); - uint16_t const exit_reason { Badge(badge).exit_reason() }; Vcpu_space::Id const vcpu_id { Badge(badge).vcpu_id() }; try { _vcpu_space().apply(vcpu_id, [&] (Nova_vcpu &vcpu) { - bool const block = vcpu._handle_exit(utcb, exit_reason); + vcpu._handle_exit(utcb); - if (block) { - Nova::reply(myself.stack_top(), vcpu._sm_sel()); + vcpu._last_resume = vcpu._resume; + if (vcpu._resume) { + nova_reply(myself, utcb); } else { - Nova::reply(myself.stack_top()); + nova_reply(myself, utcb, vcpu._sm_sel()); } }); @@ -675,44 +674,11 @@ void Nova_vcpu::_exit_entry(addr_t badge) /* somebody called us directly ? ... ignore/deny */ utcb.items = 0; utcb.mtd = 0; - Nova::reply(myself.stack_top()); + nova_reply(myself, utcb); } } -void Nova_vcpu::pause() -{ - Thread * const current = Thread::myself(); - - if (_dispatching == current) { - /* current thread is already dispatching */ - if (_block) - /* issue pause exit next time - fall through */ - _block = false; - else { - _block = true; - return; - } - } - - if ((_ep_handler == current) && _block) { - _remote = PAUSE; - /* already blocked */ - } - - if (_ep_handler != current) - _remote = PAUSE; - - if (!_ep_handler) { - /* not started yet - let startup handler issue the recall */ - return; - } - - Nova::ec_ctrl(Nova::EC_RECALL, _ec_sel()); - Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP); -} - - Signal_context_capability Nova_vcpu::_create_exit_handler(Pd_session &pd, Vcpu_handler_base &handler, uint16_t vcpu_id, @@ -758,6 +724,8 @@ Nova_vcpu::Nova_vcpu(Env &env, Vm_connection &vm, Allocator &alloc, if (_id_elem.id().value > 0xffff) throw Vcpu_id_space_exhausted(); + _ep_handler = reinterpret_cast(&handler.rpc_ep()); + uint16_t const vcpu_id = (uint16_t)_id_elem.id().value; Signal_context_capability dontcare_exit = @@ -782,14 +750,13 @@ Nova_vcpu::Nova_vcpu(Env &env, Vm_connection &vm, Allocator &alloc, ** vCPU API ** **************/ -void Vm_connection::Vcpu::run() { static_cast(_native_vcpu).run(); } -void Vm_connection::Vcpu::pause() { static_cast(_native_vcpu).pause(); } -Vcpu_state & Vm_connection::Vcpu::state() { return static_cast(_native_vcpu).state(); } +void Vm_connection::Vcpu::_with_state(Call_with_state &cw) { static_cast(_native_vcpu).with_state(cw); } Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc, Vcpu_handler_base &handler, Exit_config const &exit_config) : _native_vcpu(*new (alloc) Nova_vcpu(vm._env, vm, alloc, handler, exit_config)) -{ } - +{ + static_cast(_native_vcpu).startup(); +}