/* * \brief Syscall bindings for the NOVA microhypervisor * \author Norman Feske * \author Sebastian Sumpf * \author Alexander Boettcher * \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 _PLATFORM__NOVA_SYSCALLS_GENERIC_H_ #define _PLATFORM__NOVA_SYSCALLS_GENERIC_H_ #include namespace Nova { enum { PAGE_SIZE_LOG2 = 12, PAGE_SIZE_BYTE = 1 << PAGE_SIZE_LOG2, PAGE_MASK_ = ~(PAGE_SIZE_BYTE - 1) }; /** * NOVA system-call IDs */ enum Syscall { NOVA_CALL = 0x0, NOVA_REPLY = 0x1, NOVA_CREATE_PD = 0x2, NOVA_CREATE_EC = 0x3, NOVA_CREATE_SC = 0x4, NOVA_CREATE_PT = 0x5, NOVA_CREATE_SM = 0x6, NOVA_REVOKE = 0x7, NOVA_LOOKUP = 0x8, NOVA_EC_CTRL = 0x9, NOVA_SC_CTRL = 0xa, NOVA_PT_CTRL = 0xb, NOVA_SM_CTRL = 0xc, NOVA_ASSIGN_PCI = 0xd, NOVA_ASSIGN_GSI = 0xe, }; /** * NOVA status codes returned by system-calls */ enum Status { NOVA_OK = 0, NOVA_IPC_TIMEOUT = 1, NOVA_IPC_ABORT = 2, NOVA_INV_HYPERCALL = 3, NOVA_INV_SELECTOR = 4, NOVA_INV_PARAMETER = 5, NOVA_INV_FEATURE = 6, NOVA_INV_CPU = 7, NOVA_INVD_DEVICE_ID = 8, }; /** * Hypervisor information page */ struct Hip { struct Mem_desc { enum Type { MULTIBOOT_MODULE = -2, MICROHYPERVISOR = -1, AVAILABLE_MEMORY = 1, RESERVED_MEMORY = 2, ACPI_RECLAIM_MEMORY = 3, ACPI_NVS_MEMORY = 4 }; uint64_t const addr; uint64_t const size; Type const type; uint32_t const aux; }; uint32_t const signature; /* magic value 0x41564f4e */ uint16_t const hip_checksum; uint16_t const hip_length; uint16_t const cpu_desc_offset; uint16_t const cpu_desc_size; uint16_t const mem_desc_offset; uint16_t const mem_desc_size; uint32_t const feature_flags; uint32_t const api_version; uint32_t const sel; /* number of cap selectors */ uint32_t const sel_exc; /* number of cap selectors for exceptions */ uint32_t const sel_vm; /* number of cap selectors for VM handling */ uint32_t const sel_gsi; /* number of global system interrupts */ uint32_t const page_sizes; /* supported page sizes */ uint32_t const utcb_sizes; /* supported utcb sizes */ uint32_t const tsc_freq; /* time-stamp counter frequency in kHz */ uint32_t const bus_freq; /* bus frequency in kHz */ bool has_feature_vmx() const { return feature_flags & (1 << 1); } bool has_feature_svm() const { return feature_flags & (1 << 2); } unsigned cpu_max() const { return (mem_desc_offset - cpu_desc_offset) / cpu_desc_size; } unsigned cpus() const { unsigned cpu_num = 0; const char * cpu_desc = reinterpret_cast(this) + cpu_desc_offset; for (unsigned i = 0; i < cpu_max(); i++) { if ((*cpu_desc) & 0x1) cpu_num++; cpu_desc += cpu_desc_size; } return cpu_num; } } __attribute__((packed)); /** * Semaphore operations */ enum Sem_op { SEMAPHORE_UP = 0U, SEMAPHORE_DOWN = 1U, SEMAPHORE_DOWNZERO = 0x3U }; class Descriptor { protected: mword_t _value; /** * Assign bitfield to descriptor */ template void _assign(mword_t new_bits) { _value &= ~(MASK << SHIFT); _value |= (new_bits & MASK) << SHIFT; } /** * Query bitfield from descriptor */ template mword_t _query() const { return (_value >> SHIFT) & MASK; } public: mword_t value() const { return _value; } } __attribute__((packed)); /** * Message-transfer descriptor */ class Mtd { private: mword_t const _value; public: enum { ACDB = 1 << 0, /* eax, ecx, edx, ebx */ EBSD = 1 << 1, /* ebp, esi, edi */ ESP = 1 << 2, EIP = 1 << 3, EFL = 1 << 4, /* eflags */ FSGS = 1 << 6, CSSS = 1 << 7, QUAL = 1 << 15, /* exit qualification */ CTRL = 1 << 16, /* execution controls */ INJ = 1 << 17, /* injection info */ STA = 1 << 18, /* interruptibility state */ TSC = 1 << 19, /* time-stamp counter */ IRQ = EFL | STA | INJ | TSC, ALL = 0x000fffff & ~CTRL, }; Mtd(mword_t value) : _value(value) { } mword_t value() const { return _value; } }; class Crd : public Descriptor { protected: /** * Bitfield holding the descriptor type */ enum { TYPE_MASK = 0x3, TYPE_SHIFT = 0, BASE_SHIFT = 12, RIGHTS_MASK = 0x1f, ORDER_MASK = 0x1f, ORDER_SHIFT = 7, BASE_MASK = (~0UL) >> BASE_SHIFT, RIGHTS_SHIFT= 2 }; /** * Capability-range-descriptor types */ enum { NULL_CRD_TYPE = 0, MEM_CRD_TYPE = 1, IO_CRD_TYPE = 2, OBJ_CRD_TYPE = 3, RIGHTS_ALL = 0x1f, }; void _base(mword_t base) { _assign(base); } void _order(mword_t order) { _assign(order); } public: Crd(mword_t base, mword_t order) { _value = 0; _base(base), _order(order); } Crd(mword_t value) { _value = value; } mword_t hotspot(mword_t sel_hotspot) const { if ((value() & TYPE_MASK) == MEM_CRD_TYPE) return sel_hotspot & PAGE_MASK_; return sel_hotspot << 12; } mword_t addr() const { return base() << BASE_SHIFT; } mword_t base() const { return _query(); } mword_t order() const { return _query(); } bool is_null() const { return (_value & TYPE_MASK) == NULL_CRD_TYPE; } uint8_t type() const { return _query(); } uint8_t rights() const { return _query(); } } __attribute__((packed)); class Rights { private: bool const _readable, _writeable, _executable; public: Rights(bool readable, bool writeable, bool executable) : _readable(readable), _writeable(writeable), _executable(executable) { } Rights() : _readable(false), _writeable(false), _executable(false) {} bool readable() const { return _readable; } bool writeable() const { return _writeable; } bool executable() const { return _executable; } }; /** * Memory-capability-range descriptor */ class Mem_crd : public Crd { private: enum { EXEC_MASK = 0x1, EXEC_SHIFT = 4, WRITE_MASK = 0x1, WRITE_SHIFT = 3, READ_MASK = 0x1, READ_SHIFT = 2 }; void _rights(Rights r) { _assign(r.executable()); _assign(r.writeable()); _assign(r.readable()); } public: Mem_crd(mword_t base, mword_t order, Rights rights = Rights()) : Crd(base, order) { _rights(rights); _assign(MEM_CRD_TYPE); } Rights rights() const { return Rights(_query(), _query(), _query()); } }; /** * I/O-capability-range descriptor */ class Io_crd : public Crd { public: Io_crd(mword_t base, mword_t order) : Crd(base, order) { _assign(IO_CRD_TYPE); _assign(RIGHTS_ALL); } }; class Obj_crd : public Crd { public: enum { RIGHT_EC_RECALL = 0x1U, RIGHT_PT_CTRL = 0x1U, RIGHT_PT_CALL = 0x2U }; Obj_crd() : Crd(0, 0) { _assign(NULL_CRD_TYPE); } Obj_crd(mword_t base, mword_t order, mword_t rights = RIGHTS_ALL) : Crd(base, order) { _assign(OBJ_CRD_TYPE); _assign(rights); } }; /** * Quantum-priority descriptor */ class Qpd : public Descriptor { private: enum { PRIORITY_MASK = 0xff, PRIORITY_SHIFT = 0, QUANTUM_SHIFT = 12, QUANTUM_MASK = (~0UL) >> QUANTUM_SHIFT }; void _quantum(mword_t quantum) { _assign(quantum); } void _priority(mword_t priority) { _assign(priority); } public: enum { DEFAULT_QUANTUM = 10000, DEFAULT_PRIORITY = 1 }; Qpd(mword_t quantum = DEFAULT_QUANTUM, mword_t priority = DEFAULT_PRIORITY) { _value = 0; _quantum(quantum), _priority(priority); } mword_t quantum() const { return _query(); } mword_t priority() const { return _query(); } }; /** * User-level thread-control block */ struct Utcb { /** * Number of untyped items uses lowest 16 bit, number of typed items * uses bit 16-31, bit 32+ are ignored on 64bit */ mword_t items; Crd crd_xlt; /* receive capability-range descriptor for translation */ Crd crd_rcv; /* receive capability-range descriptor for delegation */ mword_t tls; /** * Data area * * The UTCB entries following the header hold message payload (normal * IDC operations) or architectural state (exception handling). */ union { /* message payload */ mword_t msg[]; /* exception state */ struct { mword_t mtd, instr_len, ip, flags; unsigned intr_state, actv_state, inj_info, inj_error; mword_t ax, cx, dx, bx; mword_t sp, bp, si, di; #ifdef __x86_64__ mword_t r8, r9, r10, r11, r12, r13, r14, r15; #endif unsigned long long qual[2]; /* exit qualification */ unsigned ctrl[2]; unsigned long long tsc; mword_t cr0, cr2, cr3, cr4; #ifdef __x86_64__ mword_t cr8, reserved; #endif mword_t dr7, sysenter_cs, sysenter_sp, sysenter_ip; struct { unsigned short sel, ar; unsigned limit; mword_t base; #ifdef __x86_32__ mword_t reserved; #endif } es, cs, ss, ds, fs, gs, ldtr, tr; struct { unsigned reserved0; unsigned limit; mword_t base; #ifdef __x86_32__ mword_t reserved1; #endif } gdtr, idtr; } __attribute__((packed)); }; struct Item { mword_t crd; mword_t hotspot; bool is_del() { return hotspot & 0x1; } }; /** * Set number of untyped message words * * Calling this function has the side effect of removing all typed * message items from the message buffer. */ void set_msg_word(unsigned num) { items = num; } /** * Return current number of message word in UTCB */ unsigned msg_words() { return items & 0xffffU; } /** * Return current number of message items on UTCB */ unsigned msg_items() { return items >> 16; } /** * Append message-transfer item to message buffer * * \param exception true to append the item to an exception reply */ __attribute__((warn_unused_result)) bool append_item(Crd crd, mword_t sel_hotspot, bool kern_pd = false, bool update_guest_pt = false, bool translate_map = false, bool dma_mem = false) { /* transfer items start at the end of the UTCB */ items += 1 << 16; Item *item = reinterpret_cast(this); item += (PAGE_SIZE_BYTE / sizeof(struct Item)) - msg_items(); /* check that there is enough space left on UTCB */ if (msg + msg_words() >= reinterpret_cast(item)) { items -= 1 << 16; return false; } /* map from hypervisor or current pd */ unsigned h = kern_pd ? (1 << 11) : 0; /* update guest page table */ unsigned g = update_guest_pt ? (1 << 10) : 0; /* mark memory dma able */ unsigned d = dma_mem ? (1 << 9) : 0; /* set type of delegation, either 'map' or 'translate and map' */ unsigned m = translate_map ? 2 : 1; item->hotspot = crd.hotspot(sel_hotspot) | g | h | d | m; item->crd = crd.value(); return true; } /** * Return typed item at postion i in UTCB * * \param i position of item requested, starts with 0 */ Item * get_item(const unsigned i) { if (i > (PAGE_SIZE_BYTE / sizeof(struct Item))) return 0; Item * item = reinterpret_cast(this) + (PAGE_SIZE_BYTE / sizeof(struct Item)) - i - 1; if (reinterpret_cast(item) < this->msg) return 0; return item; } mword_t mtd_value() const { return static_cast(mtd).value(); } } __attribute__((packed)); /** * Size of event-specific portal window mapped at PD creation time */ enum { 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, }; /** * Event-specific capability selectors */ enum { PT_SEL_PAGE_FAULT = 0xe, PT_SEL_PARENT = 0x1a, /* convention on Genode */ PT_SEL_STARTUP = 0x1e, PT_SEL_RECALL = 0x1f, SM_SEL_EC = 0x1d, /* convention on Genode */ }; } #endif /* _PLATFORM__NOVA_SYSCALLS_GENERIC_H_ */