#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 kThread::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 slots < 1 || !caps[0] || !caps[0]->cap (0)->target: return kCapability::Context c c.caps = NULL c.data[0] = Num (code, data) c.data[1] = 0 caps[0]->cap (0)->invoke (&c) // From user-provided, thus untrusted, data, find a capability. kCapRef kThread::find_capability (unsigned code, bool *copy): *copy = code & CAP_COPY unsigned c = code & ~CAP_COPY unsigned slot = c >> 16 unsigned num = c & 0xffff if slot >= slots || !caps[slot] || num >= caps[slot]->size: if c != CAP_NONE: panic (code, "debug") dbg_log_num ((unsigned)old_current) dbg_log (": invalid capability ") dbg_log_num (code) dbg_log_char ('\n') return kCapRef () return kCapRef (caps[slot], num) void kThread::fill_slot (unsigned slot, kCaps *new_caps): if slot >= slots: return if caps[slot]: // TODO: invalidate slot. caps[slot] = new_caps if new_caps: // TODO: link it into a list. // Try to deliver a message. bool kReceiver::try_deliver (): if !messages: return false if !owner || !owner->is_waiting (): return false kMessage *m = last_message if protected_only: for ; m; m = (kMessage *)m->prev: if m->cap_protected.value () == reply_protected_data.value (): protected_only = false break if !m: return false owner->fill_slot (owner->recv_slot, m->caps) kThread_arch_receive (owner, m->cap_protected, recv_protected, m->data) address_space->free_message (this, m) owner->unwait () return true // Send a message to a receiver; try to deliver it immediately. bool kReceiver::send_message (Num cap_protected, kCapability::Context *c): if owner && owner->is_waiting () && (cap_protected.value () == reply_protected_data.value () || !protected_only): if protected_only: protected_only = false if owner->recv_slot < owner->slots: owner->fill_slot (owner->recv_slot, c->caps) kThread_arch_receive (owner, cap_protected, recv_protected, 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. kMessage *msg = NULL; if queue_limit: msg = address_space->alloc_message (this) if msg: --queue_limit if !msg: // TODO: use sender-provided storage. if !msg: return false msg->cap_protected = cap_protected if protected_only && cap_protected.value () == reply_protected_data.value (): // Put this message in the end (where it will be first seen). Clear the protected_only flag. protected_only = false if msg->next: ((kMessage *)msg->next)->prev = NULL messages = (kMessage *)msg->next msg->next = NULL msg->prev = last_message ((kMessage *)msg->prev)->next = msg last_message = msg for unsigned i = 0; i < 2; ++i: msg->data[i] = c->data[i] msg->caps = c->caps return true static kCapRef reply static kReceiver *reply_target static Num reply_protected static void reply_num (unsigned num1, unsigned num2 = 0, unsigned num3 = 0): kCapability::Context c c.data[0] = Num (num1, num2) c.data[1] = num3 c.caps = NULL invoke (reply_target, reply_protected, &c, NULL) static void reply_cap (unsigned target, Num cap_protected, kCapRef *ref): if reply: reply.set ((kReceiver *)target, cap_protected, kCapRef (), ref) reply_num (0) static void receiver_invoke (unsigned cmd, unsigned target, Num cap_protected, kCapability::Context *c): kReceiver *receiver = (kReceiver *)cap_protected.l switch cmd: case Receiver::SET_OWNER: if c->caps->size < 2: reply_num (~0) return unsigned cap = (unsigned)c->caps->cap (1)->target if cap != (CAPTYPE_THREAD | CAP_MASTER) && cap != (CAPTYPE_THREAD | Thread::SET_OWNER): // FIXME: This makes it impossible to use a fake kThread capability. return receiver->own ((kThread *)c->caps->cap (1)->cap_protected.l) break case Receiver::CREATE_CAPABILITY: reply_cap ((unsigned)receiver, c->data[1], &receiver->capabilities) return case Receiver::CREATE_CALL_CAPABILITY: reply_cap (CAPTYPE_RECEIVER | (c->data[0].h ? Receiver::CALL_ASYNC : Receiver::CALL), cap_protected, &((kObject *)cap_protected.l)->refs) return case Receiver::GET_REPLY_PROTECTED_DATA: reply_num (receiver->reply_protected_data.l, receiver->reply_protected_data.h, receiver->protected_only ? 1 : 0) return case Receiver::SET_REPLY_PROTECTED_DATA: receiver->reply_protected_data = c->data[1] break case Receiver::GET_ALARM: reply_num (receiver->alarm_count) return case Receiver::SET_ALARM: case Receiver::ADD_ALARM: unsigned old = receiver->alarm_count if cmd == Receiver::SET_ALARM: receiver->alarm_count = c->data[1].l else: receiver->alarm_count += c->data[1].l 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 reply_num (receiver->alarm_count) return default: reply_num (ERR_INVALID_OPERATION) break reply_num (0) static void memory_invoke (unsigned cmd, unsigned target, Num cap_protected, kCapability::Context *c): kMemory *mem = (kMemory *)cap_protected.l switch cmd: case Memory::CREATE: switch c->data[0].h: case CAPTYPE_RECEIVER: kReceiver *ret = mem->alloc_receiver () if ret: reply_cap (CAPTYPE_RECEIVER | CAP_MASTER, (unsigned)ret, &ret->refs) else: reply_num (ERR_OUT_OF_MEMORY) return case CAPTYPE_MEMORY: kMemory *ret = mem->alloc_memory () if ret: reply_cap (CAPTYPE_MEMORY | CAP_MASTER, (unsigned)ret, &ret->refs) else: reply_num (ERR_OUT_OF_MEMORY) return case CAPTYPE_THREAD: kThread *ret = mem->alloc_thread (c->data[1].l) if ret: reply_cap (CAPTYPE_THREAD | CAP_MASTER, (unsigned)ret, &ret->refs) else: reply_num (ERR_OUT_OF_MEMORY) return case CAPTYPE_PAGE: kPage *ret = mem->alloc_page () if ret: reply_cap (CAPTYPE_PAGE | CAP_MASTER, (unsigned)ret, &ret->refs) else: reply_num (ERR_OUT_OF_MEMORY) return case CAPTYPE_CAPS: kCaps *ret = mem->alloc_caps (c->data[1].l) if ret: reply_cap (CAPTYPE_CAPS | CAP_MASTER, (unsigned)ret, &ret->refs) else: reply_num (ERR_OUT_OF_MEMORY) return default: reply_num (~0) return break case Memory::DESTROY: if c->caps->size < 2 || (unsigned)c->caps->cap (1)->target & ~KERNEL_MASK || !c->caps->cap (1)->target || ((kObject *)c->caps->cap (1)->cap_protected.l)->address_space != mem: reply_num (~0) return switch (unsigned)c->caps->cap (1)->target & CAPTYPE_MASK: case CAPTYPE_RECEIVER: mem->free_receiver ((kReceiver *)c->caps->cap (1)->cap_protected.l) break case CAPTYPE_MEMORY: mem->free_memory ((kMemory *)c->caps->cap (1)->cap_protected.l) break case CAPTYPE_THREAD: mem->free_thread ((kThread *)c->caps->cap (1)->cap_protected.l) break case CAPTYPE_PAGE: mem->free_page ((kPage *)c->caps->cap (1)->cap_protected.l) break case CAPTYPE_CAPS: mem->free_caps ((kCaps *)c->caps->cap (1)->cap_protected.l) break default: panic (0x55228930, "invalid case") return break case Memory::LIST: // TODO break case Memory::MAP: // FIXME: this should work for fake pages as well. if c->caps->size < 2 || (unsigned)c->caps->cap (1)->target & ~KERNEL_MASK || ((unsigned)c->caps->cap (1)->target & CAPTYPE_MASK) != CAPTYPE_PAGE: reply_num (~0) return kPage *page = (kPage *)c->caps->cap (1)->cap_protected.l if page->address_space != mem: reply_num (~0) return bool readonly = c->data[1].l & (unsigned)c->caps->cap (1)->target & Page::READONLY mem->map (page, c->data[1].l & PAGE_MASK, readonly) break case Memory::MAPPING: bool readonly kPage *page = mem->get_mapping (c->data[1].l, &readonly) unsigned t = CAPTYPE_PAGE | CAP_MASTER if readonly: t |= Page::READONLY reply_cap (t, (unsigned)page, &page->refs) return case Memory::GET_LIMIT: reply_num (mem->limit) return case Memory::SET_LIMIT: mem->limit = c->data[1].l break default: reply_num (ERR_INVALID_OPERATION) return reply_num (0) static void thread_invoke (unsigned cmd, unsigned target, Num cap_protected, kCapability::Context *c): kThread *thread = (kThread *)cap_protected.l switch cmd: case Thread::GET_INFO: switch c->data[0].h: case Thread::PC: reply_num (thread->pc) return case Thread::SP: reply_num (thread->sp) return case Thread::FLAGS: reply_num (thread->flags) return default: reply_num (*kThread_arch_info (thread, c->data[0].h)) return case Thread::SET_INFO: unsigned *value switch c->data[1].l: case Thread::PC: value = &thread->pc break case Thread::SP: value = &thread->sp break case Thread::FLAGS: // It is not possible to set the PRIV flag (but it can be reset). if c->data[1].l & Thread::PRIV: c->data[1].h &= ~Thread::PRIV value = &thread->flags if c->data[1].h & ~Thread::USER_FLAGS: unsigned v = (*value & ~c->data[1].h) | (c->data[1].l & c->data[1].h) if (v & Thread::WAITING) != (*value & Thread::WAITING): if v & Thread::WAITING: thread->wait () else thread->unwait () if (v & Thread::RUNNING) != (*value & Thread::RUNNING): if v & Thread::RUNNING: thread->run () else thread->unrun () break default: value = kThread_arch_info (thread, c->data[1].l) break if value: *value = (*value & ~c->data[1].h) | (c->data[1].l & c->data[1].h) break case Thread::SCHEDULE: do_schedule = true return if !(thread->flags & Thread::PRIV): reply_num (~0) return switch cmd: case Thread::PRIV_REGISTER_INTERRUPT: arch_register_interrupt (c->data[1].l, c->caps->size >= 2 && (((unsigned)c->caps->cap (1)->target) & ~REQUEST_MASK) == CAPTYPE_RECEIVER ? (kReceiver *)c->caps->cap (1)->cap_protected.l : NULL) break case Thread::PRIV_GET_TOP_MEMORY: reply_cap (CAPTYPE_MEMORY | CAP_MASTER, (unsigned)&top_memory, &top_memory.refs) return case Thread::PRIV_MAKE_PRIV: if c->caps->size < 2 || ((unsigned)c->caps->cap (1)->target) & ~REQUEST_MASK != CAPTYPE_THREAD: reply_num (~0) return ((kThread *)c->caps->cap (1)->cap_protected.l)->flags |= Thread::PRIV break case Thread::PRIV_ALLOC_RANGE: if c->caps->size < 2 || ((unsigned)c->caps->cap (1)->target) & ~REQUEST_MASK != CAPTYPE_MEMORY: reply_num (~0) return kMemory *mem = (kMemory *)c->caps->cap (1)->cap_protected.l if !mem->use (c->data[1].l): reply_num (ERR_OUT_OF_MEMORY) return unsigned data = phys_alloc (c->data[1].l) if !data: mem->unuse (c->data[1].l) reply_num (ERR_OUT_OF_MEMORY) return reply_num (data & ~0xc0000000) return case Thread::PRIV_ALLOC_PHYSICAL: if c->caps->size < 2 || ((unsigned)c->caps->cap (1)->target) & ~REQUEST_MASK != CAPTYPE_PAGE: reply_num (~0) return kPage *page = (kPage *)c->caps->cap (1)->cap_protected.l page->forget () if !(c->data[1].l & 2): if page->flags & Page::PAYING: page->flags &= ~Page::PAYING page->address_space->unuse () else: // This is for mapping allocated ranges. They are already paid for. Record that. if page->flags & Page::PAYING: page->address_space->unuse () else: page->flags |= Page::PAYING page->frame = c->data[1].l & PAGE_MASK page->flags |= Page::FRAME if !(c->data[1].l & 1): page->flags |= Page::UNCACHED if !(c->data[1].l & 2): page->flags |= Page::PHYSICAL kPage_arch_update_mapping (page) break case Thread::PRIV_PHYSICAL_ADDRESS: if c->caps->size < 2 || ((unsigned)c->caps->cap (1)->target) & ~REQUEST_MASK != CAPTYPE_PAGE: reply_num (~0) return kPage *page = (kPage *)c->caps->cap (1)->cap_protected.l reply_num (page->frame & ~0xc0000000) return default: reply_num (ERR_INVALID_OPERATION) return reply_num (0) return static bool page_check_payment (kPage *page): kPage *p for p = page->share_prev; p; p = p->share_prev: if p->flags & Page::PAYING: return true for p = page->share_next; p; p = p->share_next: if p->flags & Page::PAYING: return true // No kPage is paying for this frame anymore. raw_pfree (page->frame) kPage *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::SHARED | Page::FRAME) kPage_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::SHARED | Page::FRAME) kPage_arch_update_mapping (p) return false static void page_invoke (unsigned cmd, unsigned target, Num cap_protected, kCapability::Context *c): kPage *page = (kPage *)cap_protected.l switch cmd & ~Page::READONLY: case Page::SHARE: if c->caps->size < 2: // Cannot share without a target page. reply_num (~0) return if ((unsigned)c->caps->cap (0)->target & ~REQUEST_MASK) != CAPTYPE_PAGE: // FIXME: This makes it impossible to use a fake kPage capability. break kPage *t = (kPage *)c->caps->cap (0)->cap_protected.l t->forget () if c->data[0].h & Page::READONLY || cmd & Page::READONLY: t->flags |= Page::READONLY if !page->flags & Page::FRAME: break if c->data[0].h & Page::COPY: if ~t->flags & Page::PAYING: break if !(c->data[0].h & Page::FORGET) || page->flags & Page::SHARED: unsigned *d = (unsigned *)page->frame if t == page: kPage *other = page->share_next ? page->share_next : page->share_prev if !other: kPage_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::FRAME t->frame = raw_zalloc () for unsigned i = 0; i <= (c->data[0].h & ~PAGE_MASK); i += 4: ((unsigned *)t->frame)[i >> 2] = d[i >> 2] else: if t != page: t->frame = page->frame t->flags |= Page::FRAME page->frame = NULL page->flags &= ~Page::FRAME kPage_arch_update_mapping (page) kPage_arch_update_mapping (t) else: if t == page: break if c->data[0].h & Page::FORGET: if ~page->flags & Page::SHARED: if t->flags & Page::PAYING: t->frame = page->frame t->flags |= Page::FRAME page->frame = NULL page->flags &= ~Page::FRAME kPage_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 kPage_arch_update_mapping (t) break case Page::SET_FLAGS: if cmd & Page::READONLY: reply_num (~0) return // Always refuse to set reserved flags. c->data[1].h &= ~(Page::PHYSICAL | Page::UNCACHED) // Remember the old flags. unsigned old = page->flags // Compute the new flags. unsigned new_flags = (page->flags & ~c->data[1].h) | (c->data[1].l & c->data[1].h) // If we stop paying, see if the frame is still paid for. If not, free it. if ~new_flags & old & Page::PAYING: // Decrease the use counter in any case. page->address_space->unuse () if !page_check_payment (page): new_flags &= ~Page::FRAME // If we start paying, increase the use counter. if new_flags & ~old & Page::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::PAYING | Page::FRAME) if old & Page::FRAME: new_flags |= Page::FRAME // If we want a frame, see if we can get it. if ~old & new_flags & Page::FRAME: kPage *p for p = page; p; p = p->share_prev: if p->flags & Page::PAYING: break if !p: for p = page->share_next; p; p = p->share_next: if p->flags & Page::PAYING: break if !p: new_flags &= ~Page::FRAME // If we can get the new frame, get it. if ~old & new_flags & Page::FRAME: page->frame = page->address_space->zalloc () kPage_arch_update_mapping (page) break default: reply_num (ERR_INVALID_OPERATION) break static void caps_invoke (unsigned cmd, unsigned target, Num cap_protected, kCapability::Context *c): kCaps *caps = (kCapsP)cap_protected.l switch cmd: default: reply_num (ERR_INVALID_OPERATION) break static void kill_reply (kCapability *self): 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 () static void kernel_invoke (unsigned target, Num cap_protected, kCapability::Context *c, kCapability *self): // Kernel calling convention: // data[0].l is the request. // caps[0] is the reply capability // other parameters' meanings depend on the operation. if target == (CAPTYPE_RECEIVER | Receiver::CALL) || target == (CAPTYPE_RECEIVER | Receiver::CALL_ASYNC): // This is a call capability. caps->cap (0) is the capability to call. It should be replaced by the reply capability. if !c->caps || c->caps->size == 0: // No caps, so no target to call. return reply_target = (kReceiver *)cap_protected.l reply_target->protected_only = target == (CAPTYPE_RECEIVER | Receiver::CALL) kReceiver *call_target = c->caps->cap (0)->target Num call_cap_protected = c->caps->cap (0)->cap_protected if ((unsigned)call_target & ~KERNEL_MASK) != 0: // This is a user-implemented object. Create a real reply capability. c->caps->cap (0)->invalidate () c->caps->set (0, (kReceiver *)(CAPTYPE_RECEIVER | Receiver::REPLY), cap_protected, kCapRef (), &((kReceiver *)cap_protected.l)->refs) call_target->send_message (call_cap_protected, c) else if (unsigned)call_target == CAPTYPE_RECEIVER | Receiver::REPLY: // Reply capability: destroy all before invoke. kill_reply (c->caps->cap (0)) kReceiver *r = (kReceiver *)call_cap_protected.l r->send_message (r->reply_protected_data, c) else: // Kernel call: don't create actual capablities. reply_protected = reply_target->reply_protected_data c->caps->cap (0)->invalidate () kernel_invoke ((unsigned)call_target, call_cap_protected, c, NULL) return if target == CAPTYPE_RECEIVER | Receiver::REPLY: // This is a reply capability. kReceiver *r = (kReceiver *)cap_protected.l kill_reply (self) r->send_message (r->reply_protected_data, c) return reply = kCapRef (c->caps, 0) unsigned cmd if (target & REQUEST_MASK) == CAP_MASTER: if c->data[0].l & CAP_MASTER_CREATE: reply_cap (target | (c->data[0].l & REQUEST_MASK), cap_protected, &((kObject *)cap_protected.l)->refs) return cmd = c->data[0].l c->data[0].l = 0 else: cmd = target & REQUEST_MASK switch target & CAPTYPE_MASK: case CAPTYPE_RECEIVER: receiver_invoke (cmd, target, cap_protected, c) break case CAPTYPE_MEMORY: memory_invoke (cmd, target, cap_protected, c) break case CAPTYPE_THREAD: thread_invoke (cmd, target, cap_protected, c) break case CAPTYPE_PAGE: page_invoke (cmd, target, cap_protected, c) break case CAPTYPE_CAPS: caps_invoke (cmd, target, cap_protected, c) break default: panic (0x99337744, "invalid capability type invoked") return return void invoke (kReceiverP target, Num cap_protected, kCapability::Context *c, kCapability *self): if (unsigned)target & ~KERNEL_MASK: // This is not a kernel capability: send a message to the receiver. target->send_message (cap_protected, c) return // This is a kernel capability. Use a function to allow optimized call capabilities. if !c->caps || c->caps->size < 1: reply_target = NULL else: reply_target = c->caps->cap (1)->target reply_protected = c->caps->cap (1)->cap_protected kernel_invoke ((unsigned)target, cap_protected, c, self)