523 lines
16 KiB
C
523 lines
16 KiB
C
/**
|
|
* \file
|
|
* \brief Arch-generic system calls implementation.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2007, 2008, 2009, 2010, 2016, ETH Zurich.
|
|
* All rights reserved.
|
|
*
|
|
* This file is distributed under the terms in the attached LICENSE file.
|
|
* If you do not find this file, copies can be found by writing to:
|
|
* ETH Zurich D-INFK, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group.
|
|
*/
|
|
|
|
#include <kernel.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <syscall.h>
|
|
#include <barrelfish_kpi/syscalls.h>
|
|
#include <mdb/mdb_tree.h>
|
|
#include <dispatch.h>
|
|
#include <distcaps.h>
|
|
|
|
static errval_t sys_double_lookup(capaddr_t rptr, uint8_t rlevel,
|
|
capaddr_t tptr, uint8_t tlevel,
|
|
struct cte **cte);
|
|
static errval_t sys_retslot_lookup(capaddr_t cnptr, uint8_t cnlevel,
|
|
cslot_t slot, struct cte **cte);
|
|
|
|
struct sysret sys_monitor_register(capaddr_t ep_caddr)
|
|
{
|
|
errval_t err;
|
|
struct capability *ep;
|
|
err = caps_lookup_cap(&dcb_current->cspace.cap, ep_caddr, 2, &ep,
|
|
CAPRIGHTS_READ);
|
|
|
|
if(err_is_fail(err)) {
|
|
printf("Failure looking up endpoint!\n");
|
|
return SYSRET(err);
|
|
}
|
|
|
|
monitor_ep = *ep;
|
|
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
struct sysret sys_cap_has_relations(capaddr_t caddr, uint8_t level,
|
|
uint8_t mask)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *cap;
|
|
err = caps_lookup_slot(&dcb_current->cspace.cap, caddr, level, &cap,
|
|
CAPRIGHTS_READ);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err);
|
|
}
|
|
|
|
uint8_t res = 0;
|
|
if (mask & RRELS_COPY_BIT && has_copies(cap)) {
|
|
res |= RRELS_COPY_BIT;
|
|
}
|
|
if (mask & RRELS_ANCS_BIT && has_ancestors(cap)) {
|
|
res |= RRELS_ANCS_BIT;
|
|
}
|
|
if (mask & RRELS_DESC_BIT && has_descendants(cap)) {
|
|
res |= RRELS_DESC_BIT;
|
|
}
|
|
|
|
return (struct sysret) { .error = SYS_ERR_OK, .value = res };
|
|
}
|
|
|
|
struct sysret sys_monitor_remote_relations(capaddr_t root_addr, uint8_t root_level,
|
|
capaddr_t cptr, uint8_t level,
|
|
uint8_t relations, uint8_t mask)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *cte;
|
|
err = sys_double_lookup(root_addr, root_level, cptr, level, &cte);
|
|
if (err_is_fail(err)) {
|
|
printf("%s: error in double_lookup: %"PRIuERRV"\n", __FUNCTION__, err);
|
|
return SYSRET(err);
|
|
}
|
|
|
|
#ifdef TRACE_PMEM_CAPS
|
|
if (caps_should_trace(&cte->cap)) {
|
|
char buf[512];
|
|
static const char chars[] = "~~01";
|
|
#define MK01(b) ((int)((b)!=0))
|
|
#define BITC(BIT) (chars[(2*MK01(mask & BIT)+MK01(relations & BIT))])
|
|
snprintf(buf, 512, "set remote: c %c, a %c, d %c",
|
|
BITC(RRELS_COPY_BIT), BITC(RRELS_ANCS_BIT),
|
|
BITC(RRELS_DESC_BIT));
|
|
#undef BITC
|
|
#undef MK01
|
|
TRACE_CAP_MSG(buf, cte);
|
|
}
|
|
#endif
|
|
|
|
if (mask) {
|
|
mdb_set_relations(cte, relations, mask);
|
|
}
|
|
|
|
relations = 0;
|
|
if (cte->mdbnode.remote_copies) {
|
|
relations |= RRELS_COPY_BIT;
|
|
}
|
|
if (cte->mdbnode.remote_ancs) {
|
|
relations |= RRELS_ANCS_BIT;
|
|
}
|
|
if (cte->mdbnode.remote_descs) {
|
|
relations |= RRELS_DESC_BIT;
|
|
}
|
|
|
|
return (struct sysret){ .error = SYS_ERR_OK, .value = relations };
|
|
}
|
|
|
|
struct sysret sys_monitor_identify_cap(struct capability *root,
|
|
capaddr_t cptr, uint8_t level,
|
|
struct capability *retbuf)
|
|
{
|
|
struct capability *cap;
|
|
errval_t err = caps_lookup_cap(root, cptr, level, &cap, CAPRIGHTS_READ);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err_push(err, SYS_ERR_IDENTIFY_LOOKUP));
|
|
}
|
|
|
|
// XXX: Write cap data directly back to user-space
|
|
// FIXME: this should involve a pointer/range check for reliability,
|
|
// but because the monitor is inherently trusted it's not a security hole
|
|
*retbuf = *cap;
|
|
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
struct sysret sys_monitor_nullify_cap(capaddr_t cptr, uint8_t level)
|
|
{
|
|
struct capability *root = &dcb_current->cspace.cap;
|
|
struct cte *cte;
|
|
errval_t err = caps_lookup_slot(root, cptr, level, &cte,
|
|
CAPRIGHTS_READ_WRITE);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err);
|
|
}
|
|
|
|
// remove from MDB
|
|
remove_mapping(cte);
|
|
|
|
// zero-out cap entry
|
|
assert(!mdb_reachable(cte));
|
|
memset(cte, 0, sizeof(*cte));
|
|
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
struct sysret sys_monitor_domain_id(capaddr_t cptr, domainid_t domain_id)
|
|
{
|
|
struct capability *root = &dcb_current->cspace.cap;
|
|
struct capability *disp;
|
|
|
|
errval_t err = caps_lookup_cap(root, cptr, CPTR_BITS, &disp,
|
|
CAPRIGHTS_READ_WRITE);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err);
|
|
}
|
|
|
|
disp->u.dispatcher.dcb->domain_id = domain_id;
|
|
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
static errval_t sys_double_lookup(capaddr_t rptr, uint8_t rlevel,
|
|
capaddr_t tptr, uint8_t tlevel,
|
|
struct cte **cte)
|
|
{
|
|
errval_t err;
|
|
|
|
struct capability *root;
|
|
err = caps_lookup_cap(&dcb_current->cspace.cap, rptr, rlevel,
|
|
&root, CAPRIGHTS_READ);
|
|
if (err_is_fail(err)) {
|
|
return err_push(err, SYS_ERR_ROOT_CAP_LOOKUP);
|
|
}
|
|
|
|
err = caps_lookup_slot(root, tptr, tlevel, cte, CAPRIGHTS_READ);
|
|
if (err_is_fail(err)) {
|
|
return err_push(err, SYS_ERR_IDENTIFY_LOOKUP);
|
|
}
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
struct sysret sys_get_cap_owner(capaddr_t root_addr, uint8_t root_level, capaddr_t cptr, uint8_t level)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *cte;
|
|
err = sys_double_lookup(root_addr, root_level, cptr, level, &cte);
|
|
if (err_is_fail(err)) {
|
|
printf("%s: error in double_lookup: %"PRIuERRV"\n", __FUNCTION__, err);
|
|
return SYSRET(err);
|
|
}
|
|
|
|
return (struct sysret) { .error = SYS_ERR_OK, .value = cte->mdbnode.owner };
|
|
}
|
|
|
|
struct sysret sys_set_cap_owner(capaddr_t root_addr, uint8_t root_level, capaddr_t cptr, uint8_t level, coreid_t owner)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *cte;
|
|
err = sys_double_lookup(root_addr, root_level, cptr, level, &cte);
|
|
if (err_is_fail(err)) {
|
|
printf("%s: error in double_lookup: %"PRIuERRV"\n", __FUNCTION__, err);
|
|
return SYSRET(err);
|
|
}
|
|
|
|
cte->mdbnode.owner = owner;
|
|
|
|
TRACE_CAP(cte);
|
|
|
|
struct cte *pred = cte;
|
|
do {
|
|
pred->mdbnode.owner = owner;
|
|
pred = mdb_predecessor(pred);
|
|
} while (is_copy(&pred->cap, &cte->cap));
|
|
|
|
struct cte *succ = cte;
|
|
do {
|
|
succ->mdbnode.owner = owner;
|
|
succ = mdb_successor(succ);
|
|
} while (is_copy(&succ->cap, &cte->cap));
|
|
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
static void sys_lock_cap_common(struct cte *cte, bool lock)
|
|
{
|
|
struct cte *pred = cte;
|
|
do {
|
|
pred->mdbnode.locked = lock;
|
|
pred = mdb_predecessor(pred);
|
|
} while (is_copy(&pred->cap, &cte->cap));
|
|
|
|
struct cte *succ = cte;
|
|
do {
|
|
succ->mdbnode.locked = lock;
|
|
succ = mdb_successor(succ);
|
|
} while (is_copy(&succ->cap, &cte->cap));
|
|
}
|
|
|
|
struct sysret sys_lock_cap(capaddr_t root_addr, uint8_t root_level, capaddr_t target_addr, uint8_t target_level)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *target;
|
|
err = sys_double_lookup(root_addr, root_level, target_addr, target_level, &target);
|
|
if (err_is_fail(err)) {
|
|
printf("%s: error in double_lookup: %"PRIuERRV"\n", __FUNCTION__, err);
|
|
return SYSRET(err);
|
|
}
|
|
|
|
if (target->mdbnode.locked) {
|
|
return SYSRET(SYS_ERR_CAP_LOCKED);
|
|
}
|
|
|
|
TRACE_CAP(target);
|
|
|
|
sys_lock_cap_common(target, true);
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
struct sysret sys_unlock_cap(capaddr_t root_addr, uint8_t root_level, capaddr_t target_addr, uint8_t target_level)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *target;
|
|
err = sys_double_lookup(root_addr, root_level, target_addr, target_level, &target);
|
|
if (err_is_fail(err)) {
|
|
printf("%s: error in double_lookup: %"PRIuERRV"\n", __FUNCTION__, err);
|
|
return SYSRET(err);
|
|
}
|
|
|
|
TRACE_CAP(target);
|
|
|
|
// XXX: check if already unlocked? -MN
|
|
sys_lock_cap_common(target, false);
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
struct sysret sys_monitor_copy_existing(struct capability *src,
|
|
capaddr_t croot_cptr,
|
|
capaddr_t cnode_cptr,
|
|
uint8_t cnode_level,
|
|
cslot_t slot)
|
|
{
|
|
struct cte *copy = mdb_find_equal(src);
|
|
if (!copy || copy->mdbnode.in_delete) {
|
|
return SYSRET(SYS_ERR_CAP_NOT_FOUND);
|
|
}
|
|
|
|
/* lookup cspace for cnode_cptr */
|
|
struct capability *root;
|
|
errval_t err = caps_lookup_cap(&dcb_current->cspace.cap, croot_cptr, 2,
|
|
&root, CAPRIGHTS_READ_WRITE);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err_push(err, SYS_ERR_DEST_ROOTCN_LOOKUP));
|
|
}
|
|
struct cte *cnode;
|
|
err = caps_lookup_slot(root, cnode_cptr, cnode_level, &cnode,
|
|
CAPRIGHTS_READ_WRITE);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err_push(err, SYS_ERR_DEST_CNODE_LOOKUP));
|
|
}
|
|
// XXX: allow L1 Cnode here?
|
|
if (cnode->cap.type != ObjType_L2CNode) {
|
|
return SYSRET(SYS_ERR_CNODE_TYPE);
|
|
}
|
|
|
|
return SYSRET(caps_copy_to_cnode(cnode, slot, copy, false, 0, 0));
|
|
}
|
|
|
|
/**
|
|
* \brief Check whether source has overlapping descendants
|
|
*/
|
|
struct sysret sys_monitor_is_retypeable(struct capability *source, gensize_t offset,
|
|
gensize_t objsize, size_t count)
|
|
{
|
|
struct cte *next = mdb_find_greater(source, false);
|
|
if (!next || !is_ancestor(&next->cap, source)) {
|
|
// next does not exist or is not a descendant of source
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
// Here: next is descendant of source; check for overlapping descendants
|
|
// XXX: this is copied from caps_retype()
|
|
errval_t err;
|
|
int find_range_result = 0;
|
|
struct cte *found_cte = NULL;
|
|
err = mdb_find_range(get_type_root(source->type),
|
|
get_address(source) + offset,
|
|
objsize * count,
|
|
MDB_RANGE_FOUND_SURROUNDING,
|
|
&found_cte,
|
|
&find_range_result);
|
|
// this should never return an error unless we mess up the
|
|
// non-user supplied arguments
|
|
if (err_is_fail(err)) {
|
|
printk(LOG_WARN, "%s: mdb_find_range returned: %"PRIuERRV"\n", __FUNCTION__, err);
|
|
// XXX: error
|
|
return SYSRET(SYS_ERR_CAP_NOT_FOUND);
|
|
}
|
|
assert(err_is_ok(err));
|
|
// return REVOKE_FIRST, if we found a cap inside the region
|
|
// (FOUND_INNER == 2) or overlapping the region (FOUND_PARTIAL == 3)
|
|
if (find_range_result >= MDB_RANGE_FOUND_INNER) {
|
|
debug(SUBSYS_CAPS,
|
|
"%s: found existing region inside, or overlapping requested region:\n",
|
|
__FUNCTION__);
|
|
return SYSRET(SYS_ERR_REVOKE_FIRST);
|
|
}
|
|
// return REVOKE_FIRST, if we found a cap that isn't our source
|
|
// (or a copy of our source) covering the whole requested region.
|
|
else if (find_range_result == MDB_RANGE_FOUND_SURROUNDING &&
|
|
!is_copy(&found_cte->cap, source))
|
|
{
|
|
debug(SUBSYS_CAPS,
|
|
"%s: found non source region fully covering requested region\n",
|
|
__FUNCTION__);
|
|
return SYSRET(SYS_ERR_REVOKE_FIRST);
|
|
}
|
|
|
|
return SYSRET(SYS_ERR_OK);
|
|
}
|
|
|
|
struct sysret sys_monitor_delete_last(capaddr_t root_addr, uint8_t root_level,
|
|
capaddr_t target_addr, uint8_t target_level,
|
|
capaddr_t ret_cn_addr, uint8_t ret_cn_level,
|
|
cslot_t ret_slot)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *target;
|
|
err = sys_double_lookup(root_addr, root_level, target_addr, target_level, &target);
|
|
if (err_is_fail(err)) {
|
|
printf("%s: root_addr: %"PRIxCADDR", root_level: %"PRIu8
|
|
", target_addr: %"PRIxCADDR", target_level: %"PRIu8"\n",
|
|
__FUNCTION__, root_addr, root_level, target_addr, target_level);
|
|
|
|
printf("%s: error in double_lookup: %"PRIxERRV"\n", __FUNCTION__, err);
|
|
return SYSRET(err);
|
|
}
|
|
|
|
struct capability *retcn;
|
|
err = caps_lookup_cap(&dcb_current->cspace.cap, ret_cn_addr,
|
|
ret_cn_level, &retcn, CAPRIGHTS_WRITE);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err_push(err, SYS_ERR_DEST_CNODE_LOOKUP));
|
|
}
|
|
|
|
if (retcn->type != ObjType_L1CNode &&
|
|
retcn->type != ObjType_L2CNode) {
|
|
return SYSRET(SYS_ERR_DEST_CNODE_INVALID);
|
|
}
|
|
if (ret_slot > cnode_get_slots(retcn)) {
|
|
return SYSRET(SYS_ERR_SLOTS_INVALID);
|
|
}
|
|
|
|
struct cte *retslot = caps_locate_slot(get_address(retcn), ret_slot);
|
|
|
|
return SYSRET(caps_delete_last(target, retslot));
|
|
}
|
|
|
|
struct sysret sys_monitor_delete_foreigns(capaddr_t cptr, uint8_t level)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *cte;
|
|
err = caps_lookup_slot(&dcb_current->cspace.cap, cptr, level, &cte, CAPRIGHTS_READ);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err);
|
|
}
|
|
|
|
return SYSRET(caps_delete_foreigns(cte));
|
|
}
|
|
|
|
struct sysret sys_monitor_revoke_mark_tgt(capaddr_t root_addr, uint8_t root_level,
|
|
capaddr_t target_addr, uint8_t target_level)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *target;
|
|
err = sys_double_lookup(root_addr, root_level, target_addr, target_level, &target);
|
|
if (err_is_fail(err)) {
|
|
printf("%s: error in double_lookup: %"PRIuERRV"\n", __FUNCTION__, err);
|
|
return SYSRET(err);
|
|
}
|
|
|
|
return SYSRET(caps_mark_revoke(&target->cap, target));
|
|
}
|
|
|
|
struct sysret sys_monitor_revoke_mark_rels(struct capability *base)
|
|
{
|
|
return SYSRET(caps_mark_revoke(base, NULL));
|
|
}
|
|
|
|
static errval_t sys_retslot_lookup(capaddr_t cnptr, uint8_t cnlevel,
|
|
cslot_t slot, struct cte **cte)
|
|
{
|
|
errval_t err;
|
|
|
|
struct capability *retcn;
|
|
err = caps_lookup_cap(&dcb_current->cspace.cap, cnptr, cnlevel,
|
|
&retcn, CAPRIGHTS_WRITE);
|
|
if (err_is_fail(err)) {
|
|
return err_push(err, SYS_ERR_DEST_CNODE_LOOKUP);
|
|
}
|
|
|
|
if (retcn->type != ObjType_L1CNode &&
|
|
retcn->type != ObjType_L2CNode) {
|
|
return SYS_ERR_DEST_CNODE_INVALID;
|
|
}
|
|
if (slot > cnode_get_slots(retcn)) {
|
|
return SYS_ERR_SLOTS_INVALID;
|
|
}
|
|
|
|
struct cte *retslot;
|
|
retslot = caps_locate_slot(get_address(retcn), slot);
|
|
|
|
if (retslot->cap.type != ObjType_Null) {
|
|
return SYS_ERR_SLOT_IN_USE;
|
|
}
|
|
|
|
*cte = retslot;
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
struct sysret sys_monitor_delete_step(capaddr_t ret_cn_addr,
|
|
uint8_t ret_cn_level,
|
|
cslot_t ret_slot)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *retslot;
|
|
err = sys_retslot_lookup(ret_cn_addr, ret_cn_level, ret_slot, &retslot);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err);
|
|
}
|
|
|
|
return SYSRET(caps_delete_step(retslot));
|
|
}
|
|
|
|
struct sysret sys_monitor_clear_step(capaddr_t ret_cn_addr,
|
|
uint8_t ret_cn_level,
|
|
cslot_t ret_slot)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *retslot;
|
|
err = sys_retslot_lookup(ret_cn_addr, ret_cn_level, ret_slot, &retslot);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err);
|
|
}
|
|
|
|
return SYSRET(caps_clear_step(retslot));
|
|
}
|
|
|
|
struct sysret sys_monitor_reclaim_ram(capaddr_t retcn_addr,
|
|
uint8_t retcn_level,
|
|
cslot_t ret_slot)
|
|
{
|
|
errval_t err;
|
|
|
|
struct cte *retslot;
|
|
err = sys_retslot_lookup(retcn_addr, retcn_level, ret_slot, &retslot);
|
|
if (err_is_fail(err)) {
|
|
return SYSRET(err);
|
|
}
|
|
|
|
return SYSRET(caps_reclaim_ram(retslot));
|
|
}
|