aos/kernel/monitor.c
Daniel Schwyn 6d444bf552 Main handout
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-03-03 14:57:51 +01:00

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));
}