mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-11-05 08:37:30 +02:00
687 lines
22 KiB
COBOL
687 lines
22 KiB
COBOL
#pypp 0
|
|
// 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/>.
|
|
|
|
#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 || !caps[slot]->caps[num].target:
|
|
if c != CAPABILITY_NONE:
|
|
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)
|