genode/repos/base-nova/include/spec/32bit/nova/syscalls.h
Alexander Boettcher 858505918a nova: support EC time in trace subject info
The vanilla NOVA kernel solely supports tracking and exporting of execution
times per SC kernel object, but not per thread (EC object). The commit extends
to track execution times per EC in the NOVA kernel, exporting it to Genode's
'core' roottask and populating Genode's Trace::Subject_info structure.

Fixes #4481
2022-05-25 12:19:32 +02:00

476 lines
13 KiB
C++

/*
* \brief Syscall bindings for the NOVA microhypervisor
* \author Norman Feske
* \author Sebastian Sumpf
* \date 2009-12-27
*/
/*
* Copyright (c) 2009 Genode Labs
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _INCLUDE__SPEC__32BIT__NOVA__SYSCALLS_H_
#define _INCLUDE__SPEC__32BIT__NOVA__SYSCALLS_H_
#include <nova/stdint.h>
#include <nova/syscall-generic.h>
#define ALWAYS_INLINE __attribute__((always_inline))
namespace Nova {
ALWAYS_INLINE
inline unsigned eax(Syscall s, uint8_t flags, unsigned sel)
{
return sel << 8 | (flags & 0xf) << 4 | s;
}
ALWAYS_INLINE
inline uint8_t syscall_0(Syscall s, uint8_t flags, unsigned sel = 0)
{
mword_t status = eax(s, flags, sel);
asm volatile (" mov %%esp, %%ecx;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
" sysenter;"
"1:"
: "+a" (status)
:
: "ecx", "edx", "memory");
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t syscall_1(Syscall s, uint8_t flags, mword_t sel, mword_t p1,
mword_t * p2 = 0)
{
mword_t status = eax(s, flags, sel);
asm volatile (" mov %%esp, %%ecx;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
" sysenter;"
"1:"
: "+a" (status), "+D" (p1)
:
: "ecx", "edx", "memory");
if (p2) *p2 = p1;
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t syscall_2(Syscall s, uint8_t flags, unsigned sel, mword_t p1, mword_t p2)
{
mword_t status = eax(s, flags, sel);
asm volatile (" mov %%esp, %%ecx;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
" sysenter;"
"1:"
: "+a" (status)
: "D" (p1), "S" (p2)
: "ecx", "edx");
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t syscall_3(Syscall s, uint8_t flags, unsigned sel,
mword_t p1, mword_t p2, mword_t p3)
{
mword_t status = eax(s, flags, sel);
asm volatile (" push %%ebx;"
" mov %%edx, %%ebx;"
" mov %%esp, %%ecx;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
" sysenter;"
"1:"
" pop %%ebx;"
: "+a" (status), "+d" (p3)
: "D" (p1), "S" (p2)
: "ecx");
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t syscall_4(Syscall s, uint8_t flags, unsigned sel,
mword_t p1, mword_t p2, mword_t p3, mword_t p4)
{
mword_t status = eax(s, flags, sel);
asm volatile (" push %%ebp;"
" push %%ebx;"
" mov %%ecx, %%ebx;"
" mov %%esp, %%ecx;"
" mov %%edx, %%ebp;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
"sysenter;"
"1:"
" pop %%ebx;"
" pop %%ebp;"
: "+a" (status), "+c" (p3), "+d" (p4)
: "D" (p1), "S" (p2)
: "memory");
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t syscall_5(Syscall s, uint8_t flags, mword_t sel,
mword_t &p1, mword_t &p2, mword_t p3 = ~0UL)
{
mword_t status = eax(s, flags, sel);
asm volatile (" push %%ebx;"
" mov %%ecx, %%ebx;"
" mov %%esp, %%ecx;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
"sysenter;"
"1:"
" pop %%ebx;"
: "+a" (status), "+D" (p1), "+S" (p2), "+c" (p3)
:
: "edx", "memory");
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t syscall_6(Syscall s, uint8_t flags, unsigned sel,
mword_t &p1, mword_t &p2, mword_t &p3,
mword_t &p4)
{
mword_t status = eax(s, flags, sel);
asm volatile (" push %%ebp;"
" push %%ebx;"
" mov %%ecx, %%ebx;"
" mov %%esp, %%ecx;"
" mov %%edx, %%ebp;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
"sysenter;"
"1:"
" mov %%ebp, %%edx;"
" mov %%ebx, %%ecx;"
" pop %%ebx;"
" pop %%ebp;"
: "+a" (status), "+D" (p1), "+S" (p2), "+c" (p3),
"+d" (p4)
:
: "memory");
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t call(unsigned pt)
{
return syscall_1(NOVA_CALL, 0, pt, 0);
}
ALWAYS_INLINE
__attribute__((noreturn))
inline void reply(void *next_sp, unsigned sm = 0)
{
mword_t reg = eax(NOVA_REPLY, 0, sm);
asm volatile ("sysenter;"
:
: "a" (reg), "c" (next_sp)
: "memory");
__builtin_unreachable();
}
ALWAYS_INLINE
inline uint8_t create_pd(unsigned pd0, unsigned pd, Crd crd,
unsigned short lower_limit, unsigned upper_limit)
{
return syscall_3(NOVA_CREATE_PD, 0, pd0, pd, crd.value(),
upper_limit << 16 | lower_limit);
}
/**
* Create an EC.
*
* \param ec two selectors - ec && ec + 1
* First selector must be unused and second selector is
* either unused or must be a valid portal selector.
* The thread will call this portal if the PD it runs in runs
* out of kernel memory.
* \param pd selector of PD the EC will created in
* \param cpu CPU number the EC will run on
* \param utcb PD local address where the UTCB of the EC will be appear
* \param esp initial stack address
* \param evt base selector for all exception portals of the EC
* \param global if true - thread requires a SC to be runnable
* if false - thread is runnable solely if it receives a IPC
* (worker thread)
*/
ALWAYS_INLINE
inline uint8_t create_ec(mword_t ec, mword_t pd, mword_t cpu, mword_t utcb,
mword_t esp, mword_t evt, bool global = false)
{
return syscall_4(NOVA_CREATE_EC, global, ec, pd,
(cpu & 0xfff) | (utcb & ~0xfff),
esp, evt);
}
ALWAYS_INLINE
inline uint8_t util_time(Syscall const syscall, mword_t const cap,
uint8_t const op, unsigned long long &time)
{
mword_t time_h = 0, time_l = 0;
uint8_t res = syscall_5(syscall, op, cap, time_h, time_l);
time = (uint64_t(time_h) << 32ULL) | uint64_t(time_l);
return res;
}
ALWAYS_INLINE
inline uint8_t sc_ec_time(mword_t const cap_sc, mword_t const cap_ec,
unsigned long long &time_sc,
unsigned long long &time_ec)
{
mword_t time_h_sc = cap_ec, time_l_sc = 0;
mword_t time_h_ec = 0, time_l_ec = 0;
uint8_t res = syscall_6(NOVA_SC_CTRL, Sc_op::SC_EC_TIME, cap_sc,
time_h_sc, time_l_sc, time_h_ec,
time_l_ec);
time_sc = (uint64_t(time_h_sc) << 32ULL) | uint64_t(time_l_sc);
time_ec = (uint64_t(time_h_ec) << 32ULL) | uint64_t(time_l_ec);
return res;
}
ALWAYS_INLINE
inline uint8_t ec_ctrl(Ec_op op, mword_t ec = ~0UL, mword_t para = ~0UL,
Crd crd = 0)
{
if (op == EC_TIME)
return NOVA_INV_HYPERCALL;
return syscall_2(NOVA_EC_CTRL, op, ec, para, crd.value());
}
ALWAYS_INLINE
inline uint8_t ec_time(mword_t const ec, unsigned long long &time)
{
return util_time(NOVA_EC_CTRL, ec, Ec_op::EC_TIME, time);
}
ALWAYS_INLINE
inline uint8_t create_sc(unsigned sc, unsigned pd, unsigned ec, Qpd qpd)
{
return syscall_3(NOVA_CREATE_SC, 0, sc, pd, ec, qpd.value());
}
ALWAYS_INLINE
inline uint8_t pt_ctrl(mword_t pt, mword_t pt_id)
{
return syscall_1(NOVA_PT_CTRL, 0, pt, pt_id);
}
ALWAYS_INLINE
inline uint8_t create_pt(unsigned pt, unsigned pd, unsigned ec, Mtd mtd,
mword_t eip, bool id_equal_pt = true)
{
uint8_t res = syscall_4(NOVA_CREATE_PT, 0, pt, pd, ec, mtd.value(), eip);
if (!id_equal_pt || res != NOVA_OK)
return res;
return pt_ctrl(pt, pt);
}
ALWAYS_INLINE
inline uint8_t create_sm(unsigned sm, unsigned pd, mword_t cnt)
{
return syscall_3(NOVA_CREATE_SM, 0, sm, pd, cnt, 0);
}
ALWAYS_INLINE
inline uint8_t create_si(mword_t si, mword_t pd, mword_t value, mword_t sm)
{
return syscall_3(NOVA_CREATE_SM, 0, si, pd, value, sm);
}
/**
* Revoke memory, capabilities or i/o ports from a PD
*
* \param crd describes region and type of resource
* \param self also revoke from source PD iif self == true
* \param remote if true the 'pd' parameter below is used, otherwise
* current PD is used as source PD
* \param pd selector describing remote PD
* \param sm SM selector which gets an up() by the kernel if the
* memory of the current revoke invocation gets freed up
* (end of RCU period)
* \param kim keep_in_mdb - if set to true the kernel will make the
* resource inaccessible for solely for the specified pd.
* All already beforehand delegated resources will not be
* changed, e.g. revoked. All rights of the local resource
* will be removed (independent of what is specified by crd).
*/
ALWAYS_INLINE
inline uint8_t revoke(Crd crd, bool self = true, bool remote = false,
mword_t pd = 0, mword_t sm = 0, bool kim = false)
{
uint8_t flags = self ? 0x1 : 0;
if (remote)
flags |= 0x2;
if (kim)
flags |= 0x4;
mword_t value_crd = crd.value();
return syscall_5(NOVA_REVOKE, flags, sm, value_crd, pd);
}
/*
* Shortcut for revoke, where solely the local cap should be revoked and
* not all subsequent delegations of the local cap.
*/
ALWAYS_INLINE
inline uint8_t drop(Crd crd) {
return revoke(crd, true, false, 0, 0, true); }
ALWAYS_INLINE
inline uint8_t lookup(Crd &crd)
{
mword_t crd_r;
uint8_t res = syscall_1(NOVA_LOOKUP, 0, 0, crd.value(), &crd_r);
crd = Crd(crd_r);
return res;
}
ALWAYS_INLINE
inline uint8_t delegate(mword_t pd_snd, mword_t pd_dst, Crd crd_dst)
{
return syscall_2(NOVA_LOOKUP, 1, pd_snd, crd_dst.value(), pd_dst);
}
ALWAYS_INLINE
inline uint8_t sm_ctrl(unsigned sm, Sem_op op, unsigned long long timeout = 0)
{
return syscall_2(NOVA_SM_CTRL, op, sm, (mword_t)(timeout >> 32), (mword_t)timeout);
}
ALWAYS_INLINE
inline uint8_t si_ctrl(mword_t sm, Sem_op op, mword_t &value, mword_t &cnt)
{
return syscall_5(NOVA_SM_CTRL, op, sm, value, cnt);
}
ALWAYS_INLINE
inline uint8_t pd_ctrl(mword_t pd_src, Pd_op op, mword_t pd_dst,
mword_t transfer)
{
return syscall_5(NOVA_PD_CTRL, op, pd_src, pd_dst, transfer);
}
ALWAYS_INLINE
inline uint8_t pd_ctrl_debug(mword_t pd, mword_t &limit, mword_t &usage)
{
return syscall_5(NOVA_PD_CTRL, Pd_op::PD_DEBUG, pd, limit, usage);
}
ALWAYS_INLINE
inline uint8_t assign_pci(mword_t pd, mword_t mem, mword_t rid)
{
return syscall_2(NOVA_ASSIGN_PCI, 0, pd, mem, rid);
}
ALWAYS_INLINE
inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu,
mword_t &msi_addr, mword_t &msi_data,
mword_t si = ~0UL)
{
msi_addr = dev;
msi_data = cpu;
return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si);
}
ALWAYS_INLINE
inline uint8_t sc_ctrl(unsigned const sc, unsigned long long &time, uint8_t op = 0)
{
return util_time(NOVA_SC_CTRL, sc, op, time);
}
}
#endif /* _INCLUDE__SPEC__32BIT__NOVA__SYSCALLS_H_ */