2009-05-20 23:07:56 +03:00
|
|
|
#pypp 0
|
2009-06-01 15:26:42 +03:00
|
|
|
// Iris: micro-kernel for a capability-based operating system.
|
|
|
|
// invoke.ccp: Capability invocation and kernel responses.
|
|
|
|
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
|
|
|
//
|
|
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
2009-05-20 23:07:56 +03:00
|
|
|
#include "kernel.hh"
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
void kThread::raise (unsigned code, unsigned data):
|
2009-08-05 11:16:24 +03:00
|
|
|
panic (code, "raise")
|
2009-07-25 01:54:12 +03:00
|
|
|
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 ()
|
2009-08-18 00:11:15 +03:00
|
|
|
if slots < 1 || !caps[0] || !caps[0]->cap (0)->target:
|
2009-07-25 01:54:12 +03:00
|
|
|
return
|
2009-08-18 00:11:15 +03:00
|
|
|
kCapability::Context c
|
|
|
|
c.caps = NULL
|
|
|
|
c.data[0] = Num (code, data)
|
|
|
|
c.data[1] = 0
|
|
|
|
caps[0]->cap (0)->invoke (&c)
|
2009-07-25 01:54:12 +03:00
|
|
|
|
2009-07-05 11:52:44 +03:00
|
|
|
// From user-provided, thus untrusted, data, find a capability.
|
2009-08-18 00:11:15 +03:00
|
|
|
kCapRef kThread::find_capability (unsigned code, bool *copy):
|
|
|
|
*copy = code & CAP_COPY
|
|
|
|
unsigned c = code & ~CAP_COPY
|
2009-08-05 11:16:24 +03:00
|
|
|
unsigned slot = c >> 16
|
|
|
|
unsigned num = c & 0xffff
|
2009-08-13 18:28:36 +03:00
|
|
|
if slot >= slots || !caps[slot] || num >= caps[slot]->size:
|
2009-08-18 00:11:15 +03:00
|
|
|
if c != CAP_NONE:
|
2009-08-13 18:28:36 +03:00
|
|
|
panic (code, "debug")
|
2009-07-25 01:54:12 +03:00
|
|
|
dbg_log_num ((unsigned)old_current)
|
2009-08-05 11:16:24 +03:00
|
|
|
dbg_log (": invalid capability ")
|
|
|
|
dbg_log_num (code)
|
2009-07-25 01:54:12 +03:00
|
|
|
dbg_log_char ('\n')
|
2009-08-18 00:11:15 +03:00
|
|
|
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.
|
2009-05-20 23:07:56 +03:00
|
|
|
|
2009-07-05 11:52:44 +03:00
|
|
|
// Try to deliver a message.
|
2009-08-18 00:11:15 +03:00
|
|
|
bool kReceiver::try_deliver ():
|
2009-07-20 01:23:45 +03:00
|
|
|
if !messages:
|
2009-06-01 02:12:54 +03:00
|
|
|
return false
|
2009-07-20 01:23:45 +03:00
|
|
|
if !owner || !owner->is_waiting ():
|
|
|
|
return false
|
2009-08-18 00:11:15 +03:00
|
|
|
kMessage *m = last_message
|
2009-06-01 02:12:54 +03:00
|
|
|
if protected_only:
|
2009-08-18 00:11:15 +03:00
|
|
|
for ; m; m = (kMessage *)m->prev:
|
|
|
|
if m->cap_protected.value () == reply_protected_data.value ():
|
2009-07-20 01:23:45 +03:00
|
|
|
protected_only = false
|
2009-06-01 02:12:54 +03:00
|
|
|
break
|
2009-07-23 13:06:32 +03:00
|
|
|
if !m:
|
|
|
|
return false
|
2009-08-18 00:11:15 +03:00
|
|
|
owner->fill_slot (owner->recv_slot, m->caps)
|
|
|
|
kThread_arch_receive (owner, m->cap_protected, recv_protected, m->data)
|
2009-07-20 01:23:45 +03:00
|
|
|
address_space->free_message (this, m)
|
2009-06-01 02:12:54 +03:00
|
|
|
owner->unwait ()
|
|
|
|
return true
|
|
|
|
|
2009-07-05 11:52:44 +03:00
|
|
|
// Send a message to a receiver; try to deliver it immediately.
|
2009-08-18 00:11:15 +03:00
|
|
|
bool kReceiver::send_message (Num cap_protected, kCapability::Context *c):
|
|
|
|
if owner && owner->is_waiting () && (cap_protected.value () == reply_protected_data.value () || !protected_only):
|
2009-08-05 11:16:24 +03:00
|
|
|
if protected_only:
|
2009-07-25 01:54:12 +03:00
|
|
|
protected_only = false
|
2009-08-18 00:11:15 +03:00
|
|
|
if owner->recv_slot < owner->slots:
|
|
|
|
owner->fill_slot (owner->recv_slot, c->caps)
|
|
|
|
kThread_arch_receive (owner, cap_protected, recv_protected, c->data)
|
2009-07-25 01:54:12 +03:00
|
|
|
owner->unwait ()
|
|
|
|
return true
|
2009-06-01 02:12:54 +03:00
|
|
|
// The owner was not waiting, or it was not possible to deliver the message. Put it in the queue.
|
2009-08-18 00:11:15 +03:00
|
|
|
kMessage *msg = NULL;
|
|
|
|
if queue_limit:
|
|
|
|
msg = address_space->alloc_message (this)
|
|
|
|
if msg:
|
|
|
|
--queue_limit
|
|
|
|
if !msg:
|
|
|
|
// TODO: use sender-provided storage.
|
2009-06-01 02:12:54 +03:00
|
|
|
if !msg:
|
|
|
|
return false
|
2009-08-18 00:11:15 +03:00
|
|
|
msg->cap_protected = cap_protected
|
|
|
|
if protected_only && cap_protected.value () == reply_protected_data.value ():
|
2009-07-20 01:23:45 +03:00
|
|
|
// Put this message in the end (where it will be first seen). Clear the protected_only flag.
|
|
|
|
protected_only = false
|
|
|
|
if msg->next:
|
2009-08-18 00:11:15 +03:00
|
|
|
((kMessage *)msg->next)->prev = NULL
|
|
|
|
messages = (kMessage *)msg->next
|
2009-07-20 01:23:45 +03:00
|
|
|
msg->next = NULL
|
|
|
|
msg->prev = last_message
|
2009-08-18 00:11:15 +03:00
|
|
|
((kMessage *)msg->prev)->next = msg
|
2009-07-20 01:23:45 +03:00
|
|
|
last_message = msg
|
2009-08-18 00:11:15 +03:00
|
|
|
for unsigned i = 0; i < 2; ++i:
|
2009-06-10 23:54:12 +03:00
|
|
|
msg->data[i] = c->data[i]
|
2009-08-18 00:11:15 +03:00
|
|
|
msg->caps = c->caps
|
2009-06-01 02:12:54 +03:00
|
|
|
return true
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
static kCapRef reply
|
|
|
|
static kReceiver *reply_target
|
|
|
|
static Num reply_protected
|
2009-05-27 19:33:05 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|
2009-06-01 02:12:54 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
static void reply_cap (unsigned target, Num cap_protected, kCapRef *ref):
|
2009-06-01 02:12:54 +03:00
|
|
|
if reply:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply.set ((kReceiver *)target, cap_protected, kCapRef (), ref)
|
|
|
|
reply_num (0)
|
2009-05-25 01:31:35 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|
2009-05-25 01:31:35 +03:00
|
|
|
return
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|
2009-06-01 02:12:54 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
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]
|
2009-07-20 01:23:45 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
case Receiver::GET_ALARM:
|
|
|
|
reply_num (receiver->alarm_count)
|
|
|
|
return
|
|
|
|
case Receiver::SET_ALARM:
|
|
|
|
case Receiver::ADD_ALARM:
|
2009-07-20 01:23:45 +03:00
|
|
|
unsigned old = receiver->alarm_count
|
2009-08-18 00:11:15 +03:00
|
|
|
if cmd == Receiver::SET_ALARM:
|
|
|
|
receiver->alarm_count = c->data[1].l
|
2009-07-20 01:23:45 +03:00
|
|
|
else:
|
2009-08-18 00:11:15 +03:00
|
|
|
receiver->alarm_count += c->data[1].l
|
2009-07-20 01:23:45 +03:00
|
|
|
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
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (receiver->alarm_count)
|
|
|
|
return
|
2009-05-25 01:31:35 +03:00
|
|
|
default:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_INVALID_OPERATION)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (0)
|
2009-05-25 01:31:35 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
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:
|
2009-05-25 01:31:35 +03:00
|
|
|
case CAPTYPE_RECEIVER:
|
2009-08-18 00:11:15 +03:00
|
|
|
kReceiver *ret = mem->alloc_receiver ()
|
2009-05-25 01:31:35 +03:00
|
|
|
if ret:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_cap (CAPTYPE_RECEIVER | CAP_MASTER, (unsigned)ret, &ret->refs)
|
2009-05-25 01:31:35 +03:00
|
|
|
else:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_OUT_OF_MEMORY)
|
|
|
|
return
|
2009-05-25 01:31:35 +03:00
|
|
|
case CAPTYPE_MEMORY:
|
2009-08-18 00:11:15 +03:00
|
|
|
kMemory *ret = mem->alloc_memory ()
|
2009-05-25 01:31:35 +03:00
|
|
|
if ret:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_cap (CAPTYPE_MEMORY | CAP_MASTER, (unsigned)ret, &ret->refs)
|
2009-05-25 01:31:35 +03:00
|
|
|
else:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_OUT_OF_MEMORY)
|
|
|
|
return
|
2009-05-25 01:31:35 +03:00
|
|
|
case CAPTYPE_THREAD:
|
2009-08-18 00:11:15 +03:00
|
|
|
kThread *ret = mem->alloc_thread (c->data[1].l)
|
2009-05-25 01:31:35 +03:00
|
|
|
if ret:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_cap (CAPTYPE_THREAD | CAP_MASTER, (unsigned)ret, &ret->refs)
|
2009-05-25 01:31:35 +03:00
|
|
|
else:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_OUT_OF_MEMORY)
|
|
|
|
return
|
2009-05-25 01:31:35 +03:00
|
|
|
case CAPTYPE_PAGE:
|
2009-08-18 00:11:15 +03:00
|
|
|
kPage *ret = mem->alloc_page ()
|
2009-05-25 01:31:35 +03:00
|
|
|
if ret:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_cap (CAPTYPE_PAGE | CAP_MASTER, (unsigned)ret, &ret->refs)
|
2009-05-25 01:31:35 +03:00
|
|
|
else:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_OUT_OF_MEMORY)
|
|
|
|
return
|
2009-08-05 11:16:24 +03:00
|
|
|
case CAPTYPE_CAPS:
|
2009-08-18 00:11:15 +03:00
|
|
|
kCaps *ret = mem->alloc_caps (c->data[1].l)
|
2009-05-25 01:31:35 +03:00
|
|
|
if ret:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_cap (CAPTYPE_CAPS | CAP_MASTER, (unsigned)ret, &ret->refs)
|
2009-05-25 01:31:35 +03:00
|
|
|
else:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_OUT_OF_MEMORY)
|
|
|
|
return
|
2009-05-25 01:31:35 +03:00
|
|
|
default:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (~0)
|
2009-05-25 01:31:35 +03:00
|
|
|
return
|
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|
2009-05-27 15:38:52 +03:00
|
|
|
return
|
2009-08-18 00:11:15 +03:00
|
|
|
switch (unsigned)c->caps->cap (1)->target & CAPTYPE_MASK:
|
2009-05-27 15:38:52 +03:00
|
|
|
case CAPTYPE_RECEIVER:
|
2009-08-18 00:11:15 +03:00
|
|
|
mem->free_receiver ((kReceiver *)c->caps->cap (1)->cap_protected.l)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-27 15:38:52 +03:00
|
|
|
case CAPTYPE_MEMORY:
|
2009-08-18 00:11:15 +03:00
|
|
|
mem->free_memory ((kMemory *)c->caps->cap (1)->cap_protected.l)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-27 15:38:52 +03:00
|
|
|
case CAPTYPE_THREAD:
|
2009-08-18 00:11:15 +03:00
|
|
|
mem->free_thread ((kThread *)c->caps->cap (1)->cap_protected.l)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-27 15:38:52 +03:00
|
|
|
case CAPTYPE_PAGE:
|
2009-08-18 00:11:15 +03:00
|
|
|
mem->free_page ((kPage *)c->caps->cap (1)->cap_protected.l)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-08-05 11:16:24 +03:00
|
|
|
case CAPTYPE_CAPS:
|
2009-08-18 00:11:15 +03:00
|
|
|
mem->free_caps ((kCaps *)c->caps->cap (1)->cap_protected.l)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-27 15:38:52 +03:00
|
|
|
default:
|
|
|
|
panic (0x55228930, "invalid case")
|
2009-07-24 15:25:53 +03:00
|
|
|
return
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
case Memory::LIST:
|
2009-05-25 01:31:35 +03:00
|
|
|
// TODO
|
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
case Memory::MAP:
|
2009-06-01 02:12:54 +03:00
|
|
|
// FIXME: this should work for fake pages as well.
|
2009-08-18 00:11:15 +03:00
|
|
|
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
|
2009-06-01 02:12:54 +03:00
|
|
|
if page->address_space != mem:
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
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:
|
2009-05-25 01:31:35 +03:00
|
|
|
reply_num (mem->limit)
|
2009-08-18 00:11:15 +03:00
|
|
|
return
|
|
|
|
case Memory::SET_LIMIT:
|
|
|
|
mem->limit = c->data[1].l
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
|
|
|
default:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_INVALID_OPERATION)
|
|
|
|
return
|
|
|
|
reply_num (0)
|
2009-05-25 01:31:35 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
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:
|
2009-05-27 19:33:05 +03:00
|
|
|
unsigned *value
|
2009-08-18 00:11:15 +03:00
|
|
|
switch c->data[1].l:
|
|
|
|
case Thread::PC:
|
2009-05-27 19:33:05 +03:00
|
|
|
value = &thread->pc
|
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
case Thread::SP:
|
2009-05-27 19:33:05 +03:00
|
|
|
value = &thread->sp
|
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
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
|
2009-05-27 19:33:05 +03:00
|
|
|
value = &thread->flags
|
2009-08-18 00:11:15 +03:00
|
|
|
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 ()
|
2009-05-27 19:33:05 +03:00
|
|
|
else
|
2009-07-24 15:25:53 +03:00
|
|
|
thread->unwait ()
|
2009-08-18 00:11:15 +03:00
|
|
|
if (v & Thread::RUNNING) != (*value & Thread::RUNNING):
|
|
|
|
if v & Thread::RUNNING:
|
2009-05-27 19:33:05 +03:00
|
|
|
thread->run ()
|
|
|
|
else
|
|
|
|
thread->unrun ()
|
|
|
|
break
|
|
|
|
default:
|
2009-08-18 00:11:15 +03:00
|
|
|
value = kThread_arch_info (thread, c->data[1].l)
|
2009-05-27 19:33:05 +03:00
|
|
|
break
|
|
|
|
if value:
|
2009-08-18 00:11:15 +03:00
|
|
|
*value = (*value & ~c->data[1].h) | (c->data[1].l & c->data[1].h)
|
2009-05-27 19:33:05 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
case Thread::SCHEDULE:
|
2009-07-20 01:23:45 +03:00
|
|
|
do_schedule = true
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|
2009-06-01 20:22:11 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
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
|
2009-06-01 20:22:11 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
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
|
2009-05-25 01:31:35 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
static bool page_check_payment (kPage *page):
|
|
|
|
kPage *p
|
2009-08-05 11:16:24 +03:00
|
|
|
for p = page->share_prev; p; p = p->share_prev:
|
2009-08-18 00:11:15 +03:00
|
|
|
if p->flags & Page::PAYING:
|
2009-06-01 02:12:54 +03:00
|
|
|
return true
|
2009-08-05 11:16:24 +03:00
|
|
|
for p = page->share_next; p; p = p->share_next:
|
2009-08-18 00:11:15 +03:00
|
|
|
if p->flags & Page::PAYING:
|
2009-06-01 02:12:54 +03:00
|
|
|
return true
|
2009-08-18 00:11:15 +03:00
|
|
|
// No kPage is paying for this frame anymore.
|
2009-08-05 11:16:24 +03:00
|
|
|
raw_pfree (page->frame)
|
2009-08-18 00:11:15 +03:00
|
|
|
kPage *next
|
2009-08-05 11:16:24 +03:00
|
|
|
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
|
2009-08-18 00:11:15 +03:00
|
|
|
p->flags &= ~(Page::SHARED | Page::FRAME)
|
|
|
|
kPage_arch_update_mapping (p)
|
2009-08-05 11:16:24 +03:00
|
|
|
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
|
2009-08-18 00:11:15 +03:00
|
|
|
p->flags &= ~(Page::SHARED | Page::FRAME)
|
|
|
|
kPage_arch_update_mapping (p)
|
2009-06-01 02:12:54 +03:00
|
|
|
return false
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
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.
|
2009-06-01 02:12:54 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
kPage *t = (kPage *)c->caps->cap (0)->cap_protected.l
|
2009-08-05 11:16:24 +03:00
|
|
|
t->forget ()
|
2009-08-18 00:11:15 +03:00
|
|
|
if c->data[0].h & Page::READONLY || cmd & Page::READONLY:
|
|
|
|
t->flags |= Page::READONLY
|
|
|
|
if !page->flags & Page::FRAME:
|
2009-08-05 11:16:24 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
if c->data[0].h & Page::COPY:
|
|
|
|
if ~t->flags & Page::PAYING:
|
2009-06-01 02:12:54 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
if !(c->data[0].h & Page::FORGET) || page->flags & Page::SHARED:
|
2009-08-05 11:16:24 +03:00
|
|
|
unsigned *d = (unsigned *)page->frame
|
2009-06-01 02:12:54 +03:00
|
|
|
if t == page:
|
2009-08-18 00:11:15 +03:00
|
|
|
kPage *other = page->share_next ? page->share_next : page->share_prev
|
2009-08-05 11:16:24 +03:00
|
|
|
if !other:
|
2009-08-18 00:11:15 +03:00
|
|
|
kPage_arch_update_mapping (t)
|
2009-08-05 11:16:24 +03:00
|
|
|
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)
|
2009-06-01 02:12:54 +03:00
|
|
|
else:
|
2009-08-18 00:11:15 +03:00
|
|
|
t->flags |= Page::FRAME
|
2009-08-05 11:16:24 +03:00
|
|
|
t->frame = raw_zalloc ()
|
2009-08-18 00:11:15 +03:00
|
|
|
for unsigned i = 0; i <= (c->data[0].h & ~PAGE_MASK); i += 4:
|
2009-08-05 11:16:24 +03:00
|
|
|
((unsigned *)t->frame)[i >> 2] = d[i >> 2]
|
|
|
|
else:
|
|
|
|
if t != page:
|
|
|
|
t->frame = page->frame
|
2009-08-18 00:11:15 +03:00
|
|
|
t->flags |= Page::FRAME
|
2009-08-05 11:16:24 +03:00
|
|
|
page->frame = NULL
|
2009-08-18 00:11:15 +03:00
|
|
|
page->flags &= ~Page::FRAME
|
|
|
|
kPage_arch_update_mapping (page)
|
|
|
|
kPage_arch_update_mapping (t)
|
2009-06-01 02:12:54 +03:00
|
|
|
else:
|
2009-08-05 11:16:24 +03:00
|
|
|
if t == page:
|
2009-06-01 02:12:54 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
if c->data[0].h & Page::FORGET:
|
|
|
|
if ~page->flags & Page::SHARED:
|
|
|
|
if t->flags & Page::PAYING:
|
2009-08-05 11:16:24 +03:00
|
|
|
t->frame = page->frame
|
2009-08-18 00:11:15 +03:00
|
|
|
t->flags |= Page::FRAME
|
2009-08-05 11:16:24 +03:00
|
|
|
page->frame = NULL
|
2009-08-18 00:11:15 +03:00
|
|
|
page->flags &= ~Page::FRAME
|
|
|
|
kPage_arch_update_mapping (page)
|
2009-06-01 02:12:54 +03:00
|
|
|
else:
|
2009-08-05 11:16:24 +03:00
|
|
|
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)
|
2009-05-29 00:35:27 +03:00
|
|
|
else:
|
2009-08-05 11:16:24 +03:00
|
|
|
t->share_prev = page->share_prev
|
|
|
|
t->share_next = page
|
|
|
|
page->share_prev = t
|
|
|
|
if t->share_prev:
|
|
|
|
t->share_prev->share_next = t
|
2009-08-18 00:11:15 +03:00
|
|
|
kPage_arch_update_mapping (t)
|
2009-08-05 11:16:24 +03:00
|
|
|
break
|
2009-08-18 00:11:15 +03:00
|
|
|
case Page::SET_FLAGS:
|
|
|
|
if cmd & Page::READONLY:
|
|
|
|
reply_num (~0)
|
|
|
|
return
|
2009-06-08 14:46:13 +03:00
|
|
|
// Always refuse to set reserved flags.
|
2009-08-18 00:11:15 +03:00
|
|
|
c->data[1].h &= ~(Page::PHYSICAL | Page::UNCACHED)
|
2009-06-01 02:12:54 +03:00
|
|
|
// Remember the old flags.
|
2009-08-05 11:16:24 +03:00
|
|
|
unsigned old = page->flags
|
2009-06-01 02:12:54 +03:00
|
|
|
// Compute the new flags.
|
2009-08-18 00:11:15 +03:00
|
|
|
unsigned new_flags = (page->flags & ~c->data[1].h) | (c->data[1].l & c->data[1].h)
|
2009-06-01 02:12:54 +03:00
|
|
|
|
|
|
|
// If we stop paying, see if the frame is still paid for. If not, free it.
|
2009-08-18 00:11:15 +03:00
|
|
|
if ~new_flags & old & Page::PAYING:
|
2009-08-05 11:16:24 +03:00
|
|
|
// Decrease the use counter in any case.
|
|
|
|
page->address_space->unuse ()
|
|
|
|
if !page_check_payment (page):
|
2009-08-18 00:11:15 +03:00
|
|
|
new_flags &= ~Page::FRAME
|
2009-06-01 02:12:54 +03:00
|
|
|
|
|
|
|
// If we start paying, increase the use counter.
|
2009-08-18 00:11:15 +03:00
|
|
|
if new_flags & ~old & Page::PAYING:
|
2009-08-05 11:16:24 +03:00
|
|
|
if !page->address_space->use():
|
2009-06-01 02:12:54 +03:00
|
|
|
// If it doesn't work, refuse to set the flag, and refuse to allocate a frame.
|
2009-08-18 00:11:15 +03:00
|
|
|
new_flags &= ~(Page::PAYING | Page::FRAME)
|
|
|
|
if old & Page::FRAME:
|
|
|
|
new_flags |= Page::FRAME
|
2009-06-01 02:12:54 +03:00
|
|
|
|
|
|
|
// If we want a frame, see if we can get it.
|
2009-08-18 00:11:15 +03:00
|
|
|
if ~old & new_flags & Page::FRAME:
|
|
|
|
kPage *p
|
2009-08-05 11:16:24 +03:00
|
|
|
for p = page; p; p = p->share_prev:
|
2009-08-18 00:11:15 +03:00
|
|
|
if p->flags & Page::PAYING:
|
2009-08-05 11:16:24 +03:00
|
|
|
break
|
|
|
|
if !p:
|
|
|
|
for p = page->share_next; p; p = p->share_next:
|
2009-08-18 00:11:15 +03:00
|
|
|
if p->flags & Page::PAYING:
|
2009-05-29 00:35:27 +03:00
|
|
|
break
|
|
|
|
if !p:
|
2009-08-18 00:11:15 +03:00
|
|
|
new_flags &= ~Page::FRAME
|
2009-06-01 02:12:54 +03:00
|
|
|
// If we can get the new frame, get it.
|
2009-08-18 00:11:15 +03:00
|
|
|
if ~old & new_flags & Page::FRAME:
|
2009-08-05 11:16:24 +03:00
|
|
|
page->frame = page->address_space->zalloc ()
|
2009-08-18 00:11:15 +03:00
|
|
|
kPage_arch_update_mapping (page)
|
2009-05-27 20:29:21 +03:00
|
|
|
break
|
2009-05-25 01:31:35 +03:00
|
|
|
default:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_INVALID_OPERATION)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
static void caps_invoke (unsigned cmd, unsigned target, Num cap_protected, kCapability::Context *c):
|
|
|
|
kCaps *caps = (kCapsP)cap_protected.l
|
|
|
|
switch cmd:
|
2009-05-25 01:31:35 +03:00
|
|
|
default:
|
2009-08-18 00:11:15 +03:00
|
|
|
reply_num (ERR_INVALID_OPERATION)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
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):
|
2009-05-25 01:31:35 +03:00
|
|
|
// Kernel calling convention:
|
2009-08-18 00:11:15 +03:00
|
|
|
// data[0].l is the request.
|
|
|
|
// caps[0] is the reply capability
|
2009-05-25 01:31:35 +03:00
|
|
|
// other parameters' meanings depend on the operation.
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|
2009-07-20 01:23:45 +03:00
|
|
|
return
|
2009-08-18 00:11:15 +03:00
|
|
|
if target == CAPTYPE_RECEIVER | Receiver::REPLY:
|
2009-05-29 00:35:27 +03:00
|
|
|
// This is a reply capability.
|
2009-08-18 00:11:15 +03:00
|
|
|
kReceiver *r = (kReceiver *)cap_protected.l
|
|
|
|
kill_reply (self)
|
2009-06-10 23:54:12 +03:00
|
|
|
r->send_message (r->reply_protected_data, c)
|
2009-07-20 01:23:45 +03:00
|
|
|
return
|
2009-08-18 00:11:15 +03:00
|
|
|
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
|
2009-05-25 01:31:35 +03:00
|
|
|
switch target & CAPTYPE_MASK:
|
|
|
|
case CAPTYPE_RECEIVER:
|
2009-08-18 00:11:15 +03:00
|
|
|
receiver_invoke (cmd, target, cap_protected, c)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
|
|
|
case CAPTYPE_MEMORY:
|
2009-08-18 00:11:15 +03:00
|
|
|
memory_invoke (cmd, target, cap_protected, c)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
|
|
|
case CAPTYPE_THREAD:
|
2009-08-18 00:11:15 +03:00
|
|
|
thread_invoke (cmd, target, cap_protected, c)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
|
|
|
case CAPTYPE_PAGE:
|
2009-08-18 00:11:15 +03:00
|
|
|
page_invoke (cmd, target, cap_protected, c)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
2009-08-05 11:16:24 +03:00
|
|
|
case CAPTYPE_CAPS:
|
2009-08-18 00:11:15 +03:00
|
|
|
caps_invoke (cmd, target, cap_protected, c)
|
2009-05-25 01:31:35 +03:00
|
|
|
break
|
|
|
|
default:
|
|
|
|
panic (0x99337744, "invalid capability type invoked")
|
2009-07-24 15:25:53 +03:00
|
|
|
return
|
2009-07-20 01:23:45 +03:00
|
|
|
return
|
2009-05-25 01:31:35 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
void invoke (kReceiverP target, Num cap_protected, kCapability::Context *c, kCapability *self):
|
2009-05-27 19:33:05 +03:00
|
|
|
if (unsigned)target & ~KERNEL_MASK:
|
|
|
|
// This is not a kernel capability: send a message to the receiver.
|
2009-08-18 00:11:15 +03:00
|
|
|
target->send_message (cap_protected, c)
|
2009-07-20 01:23:45 +03:00
|
|
|
return
|
2009-05-25 01:31:35 +03:00
|
|
|
// This is a kernel capability. Use a function to allow optimized call capabilities.
|
2009-08-18 00:11:15 +03:00
|
|
|
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)
|