genode/repos/base-nova/include/spec/32bit/nova/syscalls.h
Norman Feske 03047009b1 base: avoid implicit conversions
This patch is a prerequisite for compiling the code with
the warnings -Wconversion enabled.

Issue #23
2021-12-17 15:04:44 +01:00

409 lines
11 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 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 ec_ctrl(Ec_op op, mword_t ec = ~0UL, mword_t para = ~0UL,
Crd crd = 0)
{
return syscall_2(NOVA_EC_CTRL, op, ec, para, crd.value());
}
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 sc, unsigned long long &time, uint8_t op = 0)
{
mword_t time_h = 0, time_l = 0;
uint8_t res = syscall_5(NOVA_SC_CTRL, op, sc, time_h, time_l);
time = time_h;
time = (time << 32ULL) | time_l;
return res;
}
}
#endif /* _INCLUDE__SPEC__32BIT__NOVA__SYSCALLS_H_ */