/* * \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 #include #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 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 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 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 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 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 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) { return syscall_1(NOVA_EC_CTRL, op, ec, para); } 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, timeout >> 32, 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_ */