#pypp 0 // Iris: micro-kernel for a capability-based operating system. // iris.hhp: header file for userspace programs. // Copyright 2009 Bas Wijnen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef __IRIS_HHP #define __IRIS_HHP // Without the standard library, we don't have this definition. // I preferred ((void*)0), but C++ has too strict type-checking to // make that work. #ifndef NULL #define NULL 0 #endif // Number of clock interrupts per second. #define HZ 100 #define PAGE_BITS (12) #define PAGE_SIZE (1 << PAGE_BITS) #define PAGE_MASK (~(PAGE_SIZE - 1)) enum Exception_code: NO_ERROR ERR_WRITE_DENIED ERR_UNMAPPED_READ ERR_UNMAPPED_WRITE ERR_INVALID_ADDRESS_READ ERR_INVALID_ADDRESS_WRITE ERR_RESERVED_INSTRUCTION ERR_COPROCESSOR_UNUSABLE ERR_OVERFLOW ERR_TRAP ERR_WATCHPOINT ERR_BREAKPOINT ERR_NO_PAGE_DIRECTORY ERR_NO_PAGE_TABLE ERR_OUT_OF_MEMORY // The following are not raised, but returned. ERR_INVALID_OPERATION NUM_EXCEPTION_CODES #ifndef NDEBUG static const char *exception_name[NUM_EXCEPTION_CODES] = { "no error", "write denied", "unmapped read", "unmapped write", "invalid address read", "invalid address write", "reserved instruction", "coprocessor unusable", "overflow", "trap", "watchpoint", "breakpoint", "no page directory", "no page table", "out of memory", "invalid operation" } #endif #define KERNEL_MASK 0xfff #define CAPTYPE_MASK 0xe00 #define REQUEST_MASK (KERNEL_MASK & ~CAPTYPE_MASK) #define CAPTYPE_INVALID 0x000 #define CAPTYPE_RECEIVER 0x200 #define CAPTYPE_MEMORY 0x400 #define CAPTYPE_THREAD 0x600 #define CAPTYPE_PAGE 0x800 #define CAPTYPE_CAPS 0xa00 //#define CAPTYPE_??? 0xc00 //#define CAPTYPE_??? 0xe00 // All kernel capabilities have a master capability, which can create others. #define CAP_MASTER 0 // Create, invoke and forget, with masked data set to 0. #define CAP_MASTER_DIRECT 0 // Master capabilities can create others. #define CAP_MASTER_CREATE (1 << 31) struct Num: unsigned l, h Num (unsigned long long n = 0) : l (n), h (n >> 32): Num (unsigned ll, unsigned hh) : l (ll), h (hh): unsigned long long value () const: return ((unsigned long long)h << 32) | l unsigned &low (): return l unsigned &high (): return h unsigned const &low () const: return l unsigned const &high () const: return h // The start function has this prototype (there is no main function). Num start () struct Cap struct Caps struct Receiver struct Thread struct Page struct Memory #define __receiver_num 0 #define __thread_num 1 #define __memory_num 2 #define __caps_num 3 #define __call_num 4 #define __parent_num 5 #define __tmp_num 6 // If this flag is set in a capability, it is copied instead of mapped. // If it is set in the target capability, the Thread waits after the request. #define CAP_COPY ((unsigned)0x80000000) // This constant signifies that no capability is passed. #define CAP_NONE (~CAP_COPY) extern Receiver __my_receiver extern Thread __my_thread extern Memory __my_memory extern Cap __my_call extern Cap __my_parent extern Caps __my_caps extern Caps __tmp_caps unsigned alloc_slot () unsigned alloc_cap () void free_slot (unsigned slot) void free_cap (Cap cap) #define __tmp_slot 1 struct Cap: unsigned code inline Cap copy () const inline Cap () explicit inline Cap (unsigned c) inline Cap (unsigned slot, unsigned idx) inline unsigned slot () const inline unsigned idx () const struct IMessage struct OMessage inline void invoke (IMessage const *i, OMessage *o) inline void call (IMessage *i, OMessage *o) inline void invoke (Cap c, Num d0 = 0, Num d1 = 0, Caps caps = __tmp_caps) inline void invoke (Num d0 = 0, Num d1 = 0) inline Num call (Cap c, Num d0 = 0, Num d1 = 0, Caps caps = __tmp_caps, unsigned slot = ~0) inline Num call (Num d0 = 0, Num d1 = 0, Caps caps = __tmp_caps, unsigned slot = ~0) inline void clone (Caps caps, unsigned idx) struct Caps : public Cap: Caps (unsigned slot, unsigned idx) : Cap (slot, idx): Caps (Cap c = Cap ()) : Cap (c): struct Cap::IMessage: Num data[2] Caps caps unsigned slot unsigned num, first Cap *set struct Cap::OMessage: Num data[2] Num cap_protected Num recv_protected Cap Cap::copy () const: return Cap (code | CAP_COPY) Cap::Cap () : code (CAP_NONE): Cap::Cap (unsigned c) : code (c): Cap::Cap (unsigned slot, unsigned idx) : code (idx | (slot << 16)): unsigned Cap::slot () const: return code >> 16 unsigned Cap::idx () const: return code & 0xffff void Cap::invoke (IMessage const *i, OMessage *o): switch i->num: default: __asm__ volatile ("lw $t9, %0" :: "m"(i->set[9].code) : "t9") case 9: __asm__ volatile ("lw $t8, %0" :: "m"(i->set[8].code) : "t8") case 8: __asm__ volatile ("lw $t7, %0" :: "m"(i->set[7].code) : "t7") case 7: __asm__ volatile ("lw $t6, %0" :: "m"(i->set[6].code) : "t6") case 6: __asm__ volatile ("lw $t5, %0" :: "m"(i->set[5].code) : "t5") case 5: __asm__ volatile ("lw $t4, %0" :: "m"(i->set[4].code) : "t4") case 4: __asm__ volatile ("lw $t3, %0" :: "m"(i->set[3].code) : "t3") case 3: __asm__ volatile ("lw $t2, %0" :: "m"(i->set[2].code) : "t2") case 2: __asm__ volatile ("lw $t1, %0" :: "m"(i->set[1].code) : "t1") case 1: __asm__ volatile ("lw $t0, %0" :: "m"(i->set[0].code) : "t0") case 0: break __asm__ volatile ("lw $v0, %2\n" "\tlw $a0, 0($v0)\n" "\tlw $a1, 4($v0)\n" "\tlw $a2, 8($v0)\n" "\tlw $a3, 12($v0)\n" "\tlw $s0, 16($v0)\n" "\tlw $s1, 20($v0)\n" "\tlw $s2, 24($v0)\n" "\tlw $s3, 28($v0)\n" "\tlw $v0, %1\n" "\tsyscall\n" "\tlw $v0, %0\n" "\tsw $a0, 0($v0)\n" "\tsw $a1, 4($v0)\n" "\tsw $a2, 8($v0)\n" "\tsw $a3, 12($v0)\n" "\tsw $s0, 16($v0)\n" "\tsw $s1, 20($v0)\n" "\tsw $s2, 24($v0)\n" "\tsw $s3, 28($v0)\n" : "=m"(o) : "m"(code), "m"(i) : "memory", "v0", "s0", "s1", "s2", "s3", "a0", "a1", "a2", "a3") void Cap::call (IMessage *i, OMessage *o): i->set[0] = *this __my_call.copy ().invoke (i, o) void Cap::invoke (Cap c, Num d0, Num d1, Caps caps): IMessage i OMessage o i.slot = ~0 i.caps = caps Cap cs[2] cs[0] = c i.set = cs i.num = 2 i.first = 0 i.data[0] = d0 i.data[1] = d1 invoke (&i, &o) void Cap::invoke (Num d0, Num d1): IMessage i OMessage o i.caps = Cap () i.slot = ~0 i.num = 0 i.data[0] = d0 i.data[1] = d1 invoke (&i, &o) Num Cap::call (Cap c, Num d0, Num d1, Caps caps, unsigned slot): IMessage i OMessage o Cap cs[2] cs[1] = c i.set = cs i.caps = caps i.slot = slot i.num = 2 i.first = 0 i.data[0] = d0 i.data[1] = d1 invoke (&i, &o) return o.data[0] Num Cap::call (Num d0, Num d1, Caps caps, unsigned slot): IMessage i OMessage o Cap cs[2] i.set = cs i.caps = caps i.slot = slot i.num = 2 i.first = 0 i.data[0] = d0 i.data[1] = d1 invoke (&i, &o) return o.data[0] void Cap::clone (Caps caps, unsigned idx): IMessage i OMessage o Cap c = this->copy () i.set = &c i.caps = caps i.first = idx i.num = 1 i.slot = ~0 i.data[0] = 0 i.data[1] = 0 invoke (&i, &o) struct Receiver : public Cap: Receiver (unsigned slot, unsigned idx) : Cap (slot, idx): Receiver (Cap c = Cap ()) : Cap (c): enum request: // Operations SET_OWNER = 1 CREATE_CAPABILITY CREATE_CALL_CAPABILITY CREATE_ASYNC_CALL_CAPABILITY GET_REPLY_PROTECTED_DATA SET_REPLY_PROTECTED_DATA GET_ALARM SET_ALARM ADD_ALARM // Reply capability. This can only be created by invoking a CALL or CALL_ASYNC capability. REPLY // A call capability. This can only be created by invoking CREATE_CALL_CAPABILITY. CALL // A call capability, waiting for only this reply is disabled. This can only be created by invoking CREATE_CALL_ASYNC_CAPABILITY. CALL_ASYNC void set_owner (Cap owner): invoke (owner, CAP_MASTER_DIRECT | Receiver::SET_OWNER) Cap create_capability (unsigned protected_data, Cap ret = Cap (0, alloc_cap ())): call (ret, CAP_MASTER_DIRECT | CREATE_CAPABILITY, protected_data) return ret Num get_reply_protected_data (): return call (CAP_MASTER_DIRECT | GET_REPLY_PROTECTED_DATA) void set_reply_protected_data (Num data): invoke (CAP_MASTER_DIRECT | SET_REPLY_PROTECTED_DATA, data) unsigned get_alarm (): return call (CAP_MASTER_DIRECT | GET_ALARM).l unsigned add_alarm (unsigned data): return call (CAP_MASTER_DIRECT | ADD_ALARM, data).l void set_alarm (unsigned data): invoke (CAP_MASTER_DIRECT | SET_ALARM, data) static inline void sleep (unsigned value, OMessage *ret, unsigned slot = __tmp_slot) Cap create_call_capability (Cap ret = Cap (0, alloc_cap ())): call (ret, CAP_MASTER_DIRECT | CREATE_CALL_CAPABILITY) return ret Cap create_async_call_capability (Cap ret = Cap (0, alloc_cap ())): call (ret, CAP_MASTER_DIRECT | CREATE_ASYNC_CALL_CAPABILITY) return ret struct Thread : public Cap: Thread (unsigned slot, unsigned idx) : Cap (slot, idx): Thread (Cap c = Cap ()) : Cap (c): enum request: // Info details are arch-specific. GET_INFO = 1 SET_INFO SCHEDULE PRIV_ALLOC_RANGE PRIV_PHYSICAL_ADDRESS PRIV_ALLOC_PHYSICAL PRIV_MAKE_PRIV PRIV_GET_TOP_MEMORY PRIV_REGISTER_INTERRUPT // This is not an operation, but having this capability allows using the thread in Receiver::set_owner. SET_OWNER // These get/set_info are not arch-specific. enum info_type: PC = ~0 SP = ~1 FLAGS = ~2 enum flags: PRIV = 1 << 31 WAITING = 1 << 30 RUNNING = 1 << 29 USER_FLAGS = ~(PRIV | WAITING | RUNNING) void make_priv (): __my_thread.invoke (*this, CAP_MASTER_DIRECT | PRIV_MAKE_PRIV) unsigned get_info (unsigned info): return call (Num (CAP_MASTER_DIRECT | GET_INFO, info)).l void set_info (unsigned info, unsigned value, unsigned mask = ~0): invoke (Num (CAP_MASTER_DIRECT | SET_INFO, info), Num (value, mask)) void set_pc (unsigned pc): set_info (PC, pc) void set_sp (unsigned sp): set_info (SP, sp) void set_flags (unsigned value, unsigned mask): set_info (FLAGS, value, mask) unsigned get_pc (): return get_info (PC) unsigned get_sp (): return get_info (SP) unsigned get_flags (): return get_info (FLAGS) void run (bool run): set_flags (run ? RUNNING : 0, RUNNING) void wait (bool wait): set_flags (wait ? WAITING : 0, WAITING) struct Page : public Cap: Page (unsigned slot, unsigned idx) : Cap (slot, idx): Page (Cap c = Cap ()) : Cap (c): enum request: SHARE = 1 GET_FLAGS SET_FLAGS // Not an operation; a capability with this bit cannot write to the page. READONLY = 8 enum share_detail: // Operation details for PAGE_SHARE // Forget the source page during the operation. This makes it a move. FORGET // Make the target independent of the source (make a copy if needed). COPY // Make the target unwritable. //READONLY: use the value from request. enum flag_values: // This is a read-only flag, which is set if the Page is shared. SHARED = 1 // When paying, the memory's use is incremented. If a frame is held, it cannot be lost. Frames are lost when the last payer forgets them. PAYING = 2 // Set if this page has a frame associated with it. This flag is automatically reset if the frame is lost because of payment problems. FRAME = 4 // A readonly page cannot be written to. This flag can not be reset while the frame is shared. The flag is already defined in request. //READONLY = 8 // This is a read-only flag, saying if this is physical memory, which mustn't be freed. PHYSICAL = 0x10 // This is a read-only flag, saying if this is uncachable memory. UNCACHED = 0x20 void share (Cap target, unsigned flags): invoke (target, CAP_MASTER_DIRECT | SHARE, flags) unsigned get_flags (): return call (CAP_MASTER_DIRECT | GET_FLAGS).l void set_flags (unsigned new_flags, unsigned mask): invoke (CAP_MASTER_DIRECT | SET_FLAGS, Num (new_flags, mask)) unsigned physical_address (): return __my_thread.call (*this, CAP_MASTER_DIRECT | Thread::PRIV_PHYSICAL_ADDRESS).l void alloc_physical (unsigned address, bool cachable, bool freeable): __my_thread.invoke (*this, CAP_MASTER_DIRECT | Thread::PRIV_ALLOC_PHYSICAL, (address & PAGE_MASK) | (cachable ? 1 : 0) | (freeable ? 2 : 0)) struct Memory : public Cap: Memory (unsigned slot, unsigned idx) : Cap (slot, idx): Memory (Cap c = Cap ()) : Cap (c): enum request: CREATE = 1 DESTROY LIST MAP MAPPING GET_LIMIT SET_LIMIT Page create_page (Page ret = Cap (0, alloc_cap ())): call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_PAGE)) return ret Thread create_thread (unsigned slots, Thread ret = Cap (0, alloc_cap ())): call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_THREAD), slots) return ret Receiver create_receiver (Receiver ret = Cap (0, alloc_cap ())): call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_RECEIVER)) return ret Memory create_memory (Memory ret = Cap (0, alloc_cap ())): call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_MEMORY)) return ret Caps create_caps (unsigned size, Caps ret = Cap (0, alloc_cap ())): call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_CAPS), size) return ret void destroy (Cap target): invoke (target, CAP_MASTER_DIRECT | DESTROY) // TODO: LIST void map (Cap page, unsigned address, bool readonly): if readonly: address |= Page::READONLY invoke (page, CAP_MASTER_DIRECT | MAP, address) Page mapping (void *address, Page ret = Cap (0, alloc_cap ())): call (ret, CAP_MASTER_DIRECT | MAPPING, Num ((unsigned)address)) return ret unsigned get_limit (unsigned limit): return call (CAP_MASTER_DIRECT | GET_LIMIT).l void set_limit (unsigned limit): invoke (CAP_MASTER_DIRECT | SET_LIMIT, limit) struct Kernel: static void wait (Cap::OMessage *o, unsigned slot = __tmp_slot): Cap::IMessage i i.slot = slot i.num = 0 Cap ().copy ().invoke (&i, o) static void schedule (): __my_thread.invoke (CAP_MASTER_DIRECT | Thread::SCHEDULE) static void register_interrupt (unsigned num): __my_thread.invoke (__my_receiver, CAP_MASTER_DIRECT | Thread::PRIV_REGISTER_INTERRUPT, num) static void unregister_interrupt (unsigned num): __my_thread.invoke (CAP_MASTER_DIRECT | Thread::PRIV_REGISTER_INTERRUPT, num) static Cap get_top_memory (): __my_thread.call (__tmp_slot, CAP_MASTER_DIRECT | Thread::PRIV_GET_TOP_MEMORY) return Cap (__tmp_slot, 0) static unsigned alloc_range (Cap memory, unsigned pages): __my_thread.call (memory, CAP_MASTER_DIRECT | Thread::PRIV_ALLOC_RANGE, pages) void Receiver::sleep (unsigned value, OMessage *ret, unsigned slot): __my_receiver.set_alarm (value) Kernel::wait (ret, slot) #if 1 // Use a define instead of an inline function, because this is better visible in disassembly, even when not optimizing. #define kdebug_char(c) do { unsigned d = (c); __asm__ volatile ("move $a0, $zero\nlw $a1, %0\nbreak" :: "m"(d) : "a0", "a1", "memory"); } while (0) #else #define kdebug_char(c) do {} while (0) #endif #define kdebug(str) do { const char *s = (str); while (*s) { kdebug_char (*s); ++s; } } while (0) static void kdebug_num (unsigned n): unsigned i const char *encode = "0123456789abcdef" for i = 0; i < 8; ++i: kdebug_char (encode[(n >> (4 * (7 - i))) & 0xf]) #endif