#pypp 0 // Iris: micro-kernel for a capability-based operating system. // invoke.ccp: Capability invocation and kernel responses. // 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 . #include "kernel.hh" void Thread::raise (unsigned code, unsigned data): panic (code, "raise") dbg_log ("raise ") dbg_log_num ((unsigned)current) dbg_log_char ('/') if code < NUM_EXCEPTION_CODES: dbg_log (exception_name[code]) else: dbg_log ("invalid code:") dbg_log_num (code) dbg_log_char ('/') dbg_log_num (data) dbg_log_char ('\n') unrun () if !caps[0] || !caps[0]->caps[0].target: return Capability::Context c for unsigned i = 0; i < 4; ++i: c.cap[i] = CapRef () c.copy[i] = false c.data[0] = code c.data[1] = data c.data[2] = 0 c.data[3] = 0 caps[0]->caps[0].invoke (&c) // From user-provided, thus untrusted, data, find a capability. CapRef Thread::find_capability (unsigned code, bool *copy): *copy = code & CAPABILITY_COPY unsigned c = code & ~CAPABILITY_COPY unsigned slot = c >> 16 unsigned num = c & 0xffff if slot >= slots || !caps[slot] || num >= caps[slot]->size: if c != CAPABILITY_NONE: panic (code, "debug") dbg_log_num ((unsigned)old_current) dbg_log (": invalid capability ") dbg_log_num (code) dbg_log_char ('\n') return CapRef () return CapRef (caps[slot], num) // Try to deliver a message. bool Receiver::try_deliver (): if !messages: return false if !owner || !owner->is_waiting (): return false Message *m = last_message if protected_only: for ; m; m = (Message *)m->prev: if m->protected_data == reply_protected_data: protected_only = false break if !m: return false for unsigned i = 0; i < 4; ++i: if owner->rcaps[i]: owner->rcaps[i]->invalidate () if m->capabilities[i] < caps->size: owner->rcaps[i].clone (CapRef (caps, m->capabilities[i]), true) Thread_arch_receive (owner, m->protected_data, m->data) address_space->free_message (this, m) owner->unwait () return true // Send a message to a receiver; try to deliver it immediately. bool Receiver::send_message (unsigned protected_data, Capability::Context *c): if owner && owner->is_waiting () && (protected_data == reply_protected_data || !protected_only): if protected_only: protected_only = false for unsigned i = 0; i < 4; ++i: if owner->rcaps[i]: owner->rcaps[i]->invalidate () if c->cap[i]: owner->rcaps[i].clone (c->cap[i], c->copy[i]) Thread_arch_receive (owner, protected_data, c->data) owner->unwait () return true // The owner was not waiting, or it was not possible to deliver the message. Put it in the queue. Message *msg = address_space->alloc_message (this) if !msg: if owner: // TODO: avoid loops. owner->raise (ERR_OUT_OF_MEMORY, 0) return false msg->protected_data = protected_data if protected_only && protected_data == reply_protected_data: // Put this message in the end (where it will be first seen). Clear the protected_only flag. protected_only = false if msg->next: ((Message *)msg->next)->prev = NULL messages = (Message *)msg->next msg->next = NULL msg->prev = last_message ((Message *)msg->prev)->next = msg last_message = msg for unsigned i = 0; i < 4; ++i: msg->data[i] = c->data[i] if !c->cap[i] || !caps: msg->capabilities[i] = 0xff else: unsigned t random = (random + 1) % caps->size for t = (random + 1) % caps->size; t != random; t = (t + 1) % caps->size: if caps->caps[t].target: continue if t == random: if owner: // TODO: avoid loops. owner->raise (ERR_OUT_OF_MEMORY, 0) for unsigned j = 0; j < i; ++j: if msg->capabilities[j] != 0xff: caps->caps[msg->capabilities[j]].invalidate () address_space->free_message (this, msg) return false msg->capabilities[i] = t caps->clone (t, c->cap[i], c->copy[i]) return true static Capability *reply static Receiver *reply_receiver static void fill_cap (CapRef r, unsigned target, unsigned protected_data): CapRef *ref if target & ~KERNEL_MASK: ref = &((Receiver *)target)->capabilities else: ref = &((Object *)protected_data)->refs r.set ((Receiver *)target, protected_data, CapRef (), ref) static void reply_cap (unsigned target, unsigned protected_data): Caps r fill_cap (CapRef (&r, 0), target, protected_data) Capability::Context c for unsigned i = 0; i < 4; ++i: c.data[i] = 0 c.cap[0] = CapRef (&r, 0) c.copy[0] = true for unsigned i = 1; i < 4; ++i: c.cap[i].reset () c.copy[i] = false if reply: reply->invoke (&c) else if reply_receiver: reply_receiver->send_message (reply_receiver->reply_protected_data, &c) r.caps[0].invalidate () static void reply_cap (CapRef cap, bool copy): Capability::Context c for unsigned i = 0; i < 4; ++i: c.data[i] = 0 c.cap[0] = cap c.copy[0] = copy for unsigned i = 1; i < 4; ++i: c.cap[i].reset () c.copy[i] = false if reply: reply->invoke (&c) else if reply_receiver: reply_receiver->send_message (reply_receiver->reply_protected_data, &c) static void reply_num (unsigned num): Capability::Context c c.data[0] = num for unsigned i = 1; i < 4; ++i: c.data[i] = 0 for unsigned i = 0; i < 4; ++i: c.cap[i].reset () c.copy[i] = false if reply: reply->invoke (&c) else if reply_receiver: reply_receiver->send_message (reply_receiver->reply_protected_data, &c) static void reply_nums (unsigned num1, unsigned num2): Capability::Context c c.data[0] = num1 c.data[1] = num2 c.data[2] = 0 c.data[3] = 0 for unsigned i = 0; i < 4; ++i: c.cap[i].reset () c.copy[i] = false if reply: reply->invoke (&c) else if reply_receiver: reply_receiver->send_message (reply_receiver->reply_protected_data, &c) static void receiver_invoke (unsigned target, unsigned protected_data, Capability::Context *c): Receiver *receiver = (Receiver *)protected_data switch c->data[0]: case CAP_RECEIVER_SET_OWNER: if !c->cap[0] || ((unsigned)c->cap[0]->target & (CAPTYPE_MASK | ~KERNEL_MASK)) != CAPTYPE_THREAD: // FIXME: This makes it impossible to use a fake Thread capability. return receiver->own ((Thread *)c->cap[0]->protected_data) break case CAP_RECEIVER_CREATE_CAPABILITY: reply_cap ((unsigned)receiver, c->data[1]) break case CAP_RECEIVER_CREATE_CALL_CAPABILITY: reply_cap (CAPTYPE_RECEIVER | (1 << CAP_RECEIVER_CALL) | (c->data[1] ? 1 << CAP_RECEIVER_CALL_ASYNC : 0), protected_data) break case CAP_RECEIVER_REPLY_PROTECTED_DATA: reply_nums (receiver->reply_protected_data, receiver->protected_only ? 1 : 0) if c->data[1]: receiver->reply_protected_data = c->data[2] break case CAP_RECEIVER_ALARM: unsigned old = receiver->alarm_count reply_num (old) if c->data[1]: receiver->alarm_count = c->data[2] else: receiver->alarm_count += c->data[2] if (old == ~0) ^ (receiver->alarm_count == ~0): // The alarm stopped or started. if old == ~0: // It started. receiver->prev_alarm = NULL receiver->next_alarm = first_alarm if receiver->next_alarm: receiver->next_alarm->prev_alarm = receiver first_alarm = receiver else: // It stopped. if receiver->prev_alarm: receiver->prev_alarm->next_alarm = receiver->next_alarm else: first_alarm = receiver->next_alarm if receiver->next_alarm: receiver->next_alarm->prev_alarm = receiver->prev_alarm break default: break static void memory_invoke (unsigned target, unsigned protected_data, Capability::Context *c): Memory *mem = (Memory *)protected_data switch c->data[0]: case CAP_MEMORY_CREATE: unsigned rights = c->data[1] & REQUEST_MASK unsigned type = c->data[1] & CAPTYPE_MASK switch type: case CAPTYPE_RECEIVER: Receiver *ret = mem->alloc_receiver () if ret: reply_cap (CAPTYPE_RECEIVER | (rights & CAP_RECEIVER_ALL_RIGHTS), (unsigned)ret) else: reply_num (0) break case CAPTYPE_MEMORY: Memory *ret = mem->alloc_memory () if ret: reply_cap (CAPTYPE_MEMORY | (rights & CAP_MEMORY_ALL_RIGHTS), (unsigned)ret) else: reply_num (0) break case CAPTYPE_THREAD: Thread *ret = mem->alloc_thread (c->data[2]) if ret: reply_cap (CAPTYPE_THREAD | (rights & CAP_THREAD_ALL_RIGHTS), (unsigned)ret) else: reply_num (0) break case CAPTYPE_PAGE: Page *ret = mem->alloc_page () if ret: reply_cap (CAPTYPE_PAGE | (rights & CAP_PAGE_ALL_RIGHTS), (unsigned)ret) else: reply_num (0) break case CAPTYPE_CAPS: Caps *ret = mem->alloc_caps (c->data[2]) if ret: reply_cap (CAPTYPE_CAPS | (rights & CAP_CAPS_ALL_RIGHTS), (unsigned)ret) else: reply_num (0) break default: return break case CAP_MEMORY_DESTROY: if !c->cap[0] || c->cap[0]->address_space != mem || (unsigned)c->cap[0]->target & ~KERNEL_MASK: return switch (unsigned)c->cap[0]->target & CAPTYPE_MASK: case CAPTYPE_RECEIVER: mem->free_receiver ((Receiver *)c->cap[0]->protected_data) break case CAPTYPE_MEMORY: mem->free_memory ((Memory *)c->cap[0]->protected_data) break case CAPTYPE_THREAD: mem->free_thread ((Thread *)c->cap[0]->protected_data) break case CAPTYPE_PAGE: mem->free_page ((Page *)c->cap[0]->protected_data) break case CAPTYPE_CAPS: mem->free_caps ((Caps *)c->cap[0]->protected_data) break default: panic (0x55228930, "invalid case") return break case CAP_MEMORY_LIST: // TODO break case CAP_MEMORY_MAP: // FIXME: this should work for fake pages as well. if !c->cap[0] || (unsigned)c->cap[0]->target & ~KERNEL_MASK || ((unsigned)c->cap[0]->target & CAPTYPE_MASK) != CAPTYPE_PAGE: break Page *page = (Page *)c->cap[0]->protected_data if page->address_space != mem: break bool writable = c->data[1] & (unsigned)c->cap[0]->target & (1 << CAP_PAGE_WRITE) mem->map (page, c->data[1] & PAGE_MASK, writable) break case CAP_MEMORY_MAPPING: bool write Page *page = mem->get_mapping (c->data[1], &write) unsigned t = CAPTYPE_PAGE | REQUEST_MASK if !write: t &= ~CAP_PAGE_WRITE reply_cap (t, (unsigned)page) break case CAP_MEMORY_LIMIT: reply_num (mem->limit) if c->data[2]: mem->limit = c->data[1] break default: break static void thread_invoke (unsigned target, unsigned protected_data, Capability::Context *c): Thread *thread = (Thread *)protected_data switch c->data[0]: case CAP_THREAD_INFO: unsigned *value switch c->data[1]: case CAP_THREAD_INFO_PC: value = &thread->pc break case CAP_THREAD_INFO_SP: value = &thread->sp break case CAP_THREAD_INFO_FLAGS: // It is not possible to set the PRIV flag, but it can be reset. c->data[2] &= ~THREAD_FLAG_PRIV value = &thread->flags if c->data[3] & ~THREAD_FLAG_USER: unsigned v = (*value & c->data[3]) | (c->data[2] & c->data[3]) if (v & THREAD_FLAG_WAITING) != (*value & THREAD_FLAG_WAITING): if v & THREAD_FLAG_WAITING: thread->wait (CapRef (), CapRef (), CapRef (), CapRef ()) else thread->unwait () if (v & THREAD_FLAG_RUNNING) != (*value & THREAD_FLAG_RUNNING): if v & THREAD_FLAG_RUNNING: thread->run () else thread->unrun () break default: value = Thread_arch_info (thread, c->data[1]) break if value: *value = (*value & ~c->data[3]) | (c->data[2] & c->data[3]) reply_num (*value) else: reply_num (0) break case CAP_THREAD_SCHEDULE: do_schedule = true break case CAP_THREAD_CAP_CLONE: reply_cap (c->cap[0], c->copy[0]) break case CAP_THREAD_PRIV: switch c->data[1]: case CAP_THREAD_PRIV_REGISTER_INTERRUPT: // Threads with access to this call are trusted, so no sanity checking is done. arch_register_interrupt (c->data[2], c->cap[0] ? (Receiver *)c->cap[0]->protected_data : NULL) break case CAP_THREAD_PRIV_GET_TOP_MEMORY: // Threads with access to this call are trusted, so no sanity checking is done. reply_cap (CAPTYPE_MEMORY | (c->data[2] & CAP_MEMORY_ALL_RIGHTS), (unsigned)&top_memory) break case CAP_THREAD_PRIV_MAKE_PRIV: // Threads with access to this call are trusted, so no sanity checking is done. if c->data[2] & THREAD_FLAG_PRIV: ((Thread *)c->cap[0]->protected_data)->flags |= THREAD_FLAG_PRIV reply_cap (CAPTYPE_THREAD | (c->data[2] & CAP_THREAD_ALL_PRIV_RIGHTS), c->cap[0]->protected_data) break case CAP_THREAD_PRIV_ALLOC_RANGE: // Threads with access to this call are trusted, so not much sanity checking is done. Memory *mem = (Memory *)c->cap[1]->protected_data if !mem->use (c->data[2]): reply_num (0) break unsigned data = phys_alloc (c->data[2]) if !data: mem->unuse (c->data[2]) reply_num (0) break reply_num (data - 0x80000000) break case CAP_THREAD_PRIV_ALLOC_PHYSICAL: // Threads with access to this call are trusted, so no sanity checking is done. Page *page = (Page *)c->cap[0]->protected_data page->forget () if !(c->data[3] & 2): if page->flags & PAGE_FLAG_PAYING: page->flags &= ~PAGE_FLAG_PAYING page->address_space->unuse () else: // This is for mapping allocated ranges. They are already paid for. Record that. if page->flags & PAGE_FLAG_PAYING: page->address_space->unuse () else: page->flags |= PAGE_FLAG_PAYING page->frame = c->data[2] page->flags |= PAGE_FLAG_FRAME if !(c->data[3] & 1): page->flags |= PAGE_FLAG_UNCACHED if !(c->data[3] & 2): page->flags |= PAGE_FLAG_PHYSICAL Page_arch_update_mapping (page) break case CAP_THREAD_PRIV_PHYSICAL_ADDRESS: // Threads with access to this call are trusted, so no sanity checking is done. Page *page = (Page *)c->cap[1]->protected_data reply_num (page->frame & ~0xc0000000) break default: break default: break static bool page_check_payment (Page *page): Page *p for p = page->share_prev; p; p = p->share_prev: if p->flags & PAGE_FLAG_PAYING: return true for p = page->share_next; p; p = p->share_next: if p->flags & PAGE_FLAG_PAYING: return true // No Page is paying for this frame anymore. raw_pfree (page->frame) Page *next for p = page->share_prev, next = p->share_prev; p; p = next, next = p->share_prev: p->frame = NULL p->share_prev = NULL p->share_next = NULL p->flags &= ~(PAGE_FLAG_SHARED | PAGE_FLAG_FRAME) Page_arch_update_mapping (p) for p = page, next = p->share_next; p; p = next, next = p->share_next: p->frame = NULL p->share_prev = NULL p->share_next = NULL p->flags &= ~(PAGE_FLAG_SHARED | PAGE_FLAG_FRAME) Page_arch_update_mapping (p) return false static void page_invoke (unsigned target, unsigned protected_data, Capability::Context *c): Page *page = (Page *)protected_data switch c->data[0]: case CAP_PAGE_SHARE: if !c->cap[0] || ((unsigned)c->cap[0]->target & ~REQUEST_MASK) != CAPTYPE_PAGE: // FIXME: This makes it impossible to use a fake Page capability. break Page *t = (Page *)c->cap[0]->protected_data t->forget () if c->data[1] & PAGE_SHARE_READONLY: t->flags &= ~PAGE_FLAG_WRITABLE if !page->flags & PAGE_FLAG_FRAME: break if c->data[1] & PAGE_SHARE_COPY: if ~t->flags & PAGE_FLAG_PAYING: break if ~c->data[1] & PAGE_SHARE_FORGET || page->flags & PAGE_FLAG_SHARED: unsigned *d = (unsigned *)page->frame if t == page: Page *other = page->share_next ? page->share_next : page->share_prev if !other: Page_arch_update_mapping (t) break if page->share_next: page->share_next->share_prev = page->share_prev if page->share_prev: page->share_prev->share_next = page->share_next page->share_next = NULL page->share_prev = NULL page_check_payment (other) else: t->flags |= PAGE_FLAG_FRAME t->frame = raw_zalloc () for unsigned i = 0; i <= (c->data[1] & ~PAGE_MASK); i += 4: ((unsigned *)t->frame)[i >> 2] = d[i >> 2] else: if t != page: t->frame = page->frame t->flags |= PAGE_FLAG_FRAME page->frame = NULL page->flags &= ~PAGE_FLAG_FRAME Page_arch_update_mapping (page) Page_arch_update_mapping (t) else: if t == page: break if c->data[1] & PAGE_SHARE_FORGET: if ~page->flags & PAGE_FLAG_SHARED: if t->flags & PAGE_FLAG_PAYING: t->frame = page->frame t->flags |= PAGE_FLAG_FRAME page->frame = NULL page->flags &= ~PAGE_FLAG_FRAME Page_arch_update_mapping (page) else: t->share_prev = page->share_prev t->share_next = page->share_next if t->share_prev: t->share_prev->share_next = t if t->share_next: t->share_next->share_prev = t page->share_prev = NULL page->share_next = NULL page->forget () page_check_payment (t) else: t->share_prev = page->share_prev t->share_next = page page->share_prev = t if t->share_prev: t->share_prev->share_next = t Page_arch_update_mapping (t) break case CAP_PAGE_FLAGS: // Always refuse to set reserved flags. c->data[2] &= ~(PAGE_FLAG_PHYSICAL | PAGE_FLAG_UNCACHED) // Remember the old flags. unsigned old = page->flags // Compute the new flags. unsigned new_flags = (page->flags & ~c->data[2]) | (c->data[1] & c->data[2]) // If we stop paying, see if the frame is still paid for. If not, free it. if ~new_flags & old & PAGE_FLAG_PAYING: // Decrease the use counter in any case. page->address_space->unuse () if !page_check_payment (page): new_flags &= ~PAGE_FLAG_FRAME // If we start paying, increase the use counter. if new_flags & ~old & PAGE_FLAG_PAYING: if !page->address_space->use(): // If it doesn't work, refuse to set the flag, and refuse to allocate a frame. new_flags &= ~(PAGE_FLAG_PAYING | PAGE_FLAG_FRAME) if old & PAGE_FLAG_FRAME: new_flags |= PAGE_FLAG_FRAME // If we want a frame, see if we can get it. if ~old & new_flags & PAGE_FLAG_FRAME: Page *p for p = page; p; p = p->share_prev: if p->flags & PAGE_FLAG_PAYING: break if !p: for p = page->share_next; p; p = p->share_next: if p->flags & PAGE_FLAG_PAYING: break if !p: new_flags &= ~PAGE_FLAG_FRAME // If we can get the new frame, get it. if ~old & new_flags & PAGE_FLAG_FRAME: page->frame = page->address_space->zalloc () Page_arch_update_mapping (page) break default: break static void caps_invoke (unsigned target, unsigned protected_data, Capability::Context *c): Caps *caps = (CapsP)protected_data switch c->data[0]: case CAP_CAPS_SET: if c->data[1] >= caps->size: break caps->caps[c->data[1]].invalidate () caps->clone (c->data[1], c->cap[0], c->copy[0]) break default: break static void kernel_invoke (unsigned target, unsigned protected_data, Capability::Context *c, Capability *self): // Kernel calling convention: // data[0] is the request. // cap[0] is the reply capability // other parameters' meanings depend on the operation. if (target & (CAPTYPE_MASK | (1 << CAP_RECEIVER_CALL))) == (CAPTYPE_RECEIVER | (1 << CAP_RECEIVER_CALL)): // This is a call capability. cap[0] is the capability to call. reply_receiver = (Receiver *)protected_data reply_receiver->protected_only = !(target & (1 << CAP_RECEIVER_CALL_ASYNC)) CapRef c0 = c->cap[0] if c0: if ((unsigned)c0->target & ~KERNEL_MASK) != 0: Caps r fill_cap (CapRef (&r, 0), protected_data, reply_receiver->reply_protected_data) c->cap[0] = CapRef (&r, 0) c->copy[0] = true c0->target->send_message (c0->protected_data, c) r.caps[0].invalidate () else: // Kernel call: don't create actual capablities. c->cap[0].reset () kernel_invoke ((unsigned)c0->target, c0->protected_data, c, c0.deref ()) return if (target & (CAPTYPE_MASK | (1 << CAP_RECEIVER_REPLY))) == (CAPTYPE_RECEIVER | (1 << CAP_RECEIVER_REPLY)): // This is a reply capability. Receiver *r = (Receiver *)protected_data r->send_message (r->reply_protected_data, c) while self->parent: self = self->parent.deref () while self->sibling_prev: self->sibling_prev->invalidate () while self->sibling_next: self->sibling_next->invalidate () self->invalidate () return if !((1 << c->data[0]) & target & REQUEST_MASK): // You are not allowed to perform this operation. dbg_log ("operation not allowed: ") dbg_log_num (c->data[0]) dbg_log ("; target = ") dbg_log_num (target) return reply = c->cap[0].deref () if c->data[0] == CAP_DEGRADE: reply_cap (target & c->data[1], protected_data) return switch target & CAPTYPE_MASK: case CAPTYPE_RECEIVER: receiver_invoke (target, protected_data, c) break case CAPTYPE_MEMORY: memory_invoke (target, protected_data, c) break case CAPTYPE_THREAD: thread_invoke (target, protected_data, c) break case CAPTYPE_PAGE: page_invoke (target, protected_data, c) break case CAPTYPE_CAPS: caps_invoke (target, protected_data, c) break default: panic (0x99337744, "invalid capability type invoked") return return void Capability::invoke (Capability::Context *c): if (unsigned)target & ~KERNEL_MASK: // This is not a kernel capability: send a message to the receiver. target->send_message (protected_data, c) return // This is a kernel capability. Use a function to allow optimized call capabilities. reply_receiver = NULL kernel_invoke ((unsigned)target, protected_data, c, this)