/**
 * \file
 * \brief Capability system user code
 */

/*
 * Copyright (c) 2007-2010, 2012, 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 <stdint.h>
#include <stdbool.h>
#include <aos/aos.h>
#include <aos/cspace.h>
#include <aos/caddr.h>
#include <aos/kernel_cap_invocations.h>
#include <aos/lmp_endpoints.h>
#include <aos/aos_rpc.h>
#include <stdio.h>

/// Root CNode
#define ROOT_CNODE_INIT { \
    .croot = CPTR_ROOTCN, \
    .cnode = 0, \
    .level = CNODE_TYPE_ROOT, }

struct cnoderef cnode_root = ROOT_CNODE_INIT;

#define TASK_CNODE_INIT { \
    .croot = CPTR_ROOTCN, \
    .cnode = CPTR_TASKCN_BASE, \
    .level = CNODE_TYPE_OTHER, }

#define PAGE_CNODE_INIT { \
    .croot = CPTR_ROOTCN, \
    .cnode = CPTR_PAGECN_BASE, \
    .level = CNODE_TYPE_OTHER, }

#define MODULE_CNODE_INIT { \
    .croot = CPTR_ROOTCN, \
    .cnode = CPTR_MODULECN_BASE, \
    .level = CNODE_TYPE_OTHER, }

/// Task CNode
struct cnoderef cnode_task = TASK_CNODE_INIT;

/// Base CNode
struct cnoderef cnode_base = {
    .cnode = CPTR_BASE_PAGE_CN_BASE,
    .level = CNODE_TYPE_OTHER,
    .croot = CPTR_ROOTCN,
};

/// Super CNode
struct cnoderef cnode_super = {
    .cnode = CPTR_SUPERCN_BASE,
    .level = CNODE_TYPE_OTHER,
    .croot = CPTR_ROOTCN,
};

/// Page CNode
struct cnoderef cnode_page = PAGE_CNODE_INIT;

/// Module CNode
struct cnoderef cnode_module = MODULE_CNODE_INIT;

struct capref cap_mmstrings = {
    .cnode = MODULE_CNODE_INIT,
    .slot = 0
};

/// Capability to Root CNode
struct capref cap_root = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_ROOTCN
};

/// Capability for IRQ table
struct capref cap_irq = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_IRQ
};

/// Capability for device memory
struct capref cap_io_dev = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_DEV
};

/// Capability for endpoint to self
struct capref cap_selfep = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_SELFEP
};

/// Capability for dispatcher
struct capref cap_dispatcher = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_DISPATCHER
};

/// Capability for dispatcher
struct capref cap_dispframe = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_DISPFRAME
};

/// Capability for ArgSpace
struct capref cap_argcn = {
    .cnode = ROOT_CNODE_INIT,
    .slot  = ROOTCN_SLOT_ARGCN
};

/// Capability for monitor endpoint
struct capref cap_monitorep = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_MONITOREP
};

/// Capability for bootinfo (only in monitor)
struct capref cap_bootinfo = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_BOOTINFO
};

/// Capability for kernel (only in monitor)
struct capref cap_kernel = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_KERNELCAP
};

/// Capability for IPI sending (only in monitor)
struct capref cap_ipi = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_IPI
};

/// PerfMon CNode
struct capref cap_perfmon = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_PERF_MON
};

/// Capability for endpoint to init (only in monitor/mem_serv)
struct capref cap_initep = {
    .cnode = TASK_CNODE_INIT,
    .slot  = TASKCN_SLOT_INITEP
};

/// Capability to the URPC frame
struct capref cap_urpc = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_MON_URPC
};

/// Session ID
struct capref cap_sessionid = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_SESSIONID
};

/// Process manager cap, allows creating domains.
struct capref cap_procmng = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_PROC_MNG
};

/// Domain ID cap.
struct capref cap_domainid = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_DOMAINID
};

/// Init endpoint cap.
struct capref cap_chan_init = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_CHAN_INIT
};

/// Memory server endpoint cap.
struct capref cap_chan_memory = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_CHAN_MEMORY
};

/// Serial server endpoint cap.
struct capref cap_chan_serial = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_CHAN_SERIAL
};

/// Process server endpoint cap.
struct capref cap_chan_process = {
    .cnode = TASK_CNODE_INIT,
    .slot = TASKCN_SLOT_CHAN_PROCESS
};

/// Monitor server endpoint cap.
struct capref cap_chan_monitor = {
        .cnode = TASK_CNODE_INIT,
        .slot = TASKCN_SLOT_CHAN_MONITOR
};

/// Root PML4 VNode
struct capref cap_vroot = {
    .cnode = PAGE_CNODE_INIT,
    .slot = PAGECN_SLOT_VROOT,
};

static inline bool backoff(int count)
{
    // very crude exponential backoff based upon core id
    int yieldcnt = 2^count * disp_get_core_id();
    for (int i=0; i<yieldcnt; i++) {
        thread_yield();
    }
    return true;
}

/**
 * \brief Retype a capability into one or more new capabilities, going through
 * the monitor to ensure consistancy with other cores.  Only necessary for
 * caps that have been sent remotely.
 */
static errval_t cap_retype_remote(struct capref src_root, struct capref dest_root,
                                  capaddr_t src, gensize_t offset, enum objtype new_type,
                                  gensize_t objsize, size_t count, capaddr_t to,
                                  capaddr_t slot, int to_level)
{
    return LIB_ERR_NOT_IMPLEMENTED;
}


/**
 * \brief Delete the given capability, going through  the monitor to ensure
 * consistancy with other cores.  Only necessary for caps that have been sent
 * remotely.
 *
 * \param cap Capability to be deleted
 *
 * Deletes (but does not revoke) the given capability, allowing the CNode slot
 * to be reused.
 */
static errval_t cap_delete_remote(struct capref root, capaddr_t src, uint8_t level)
{
    return LIB_ERR_NOT_IMPLEMENTED;
}

/**
 * \brief Revoke (delete all copies and descendants of) the given capability,
 * going through the monitor to ensure consistancy with other cores.  Only
 * necessary for caps that have been sent remotely.
 *
 * \param cap Capability to be revoked
 *
 * Deletes all copies and descendants of the given capability, but not the
 * capability itself. If this succeeds, the capability is guaranteed to be
 * the only copy in the system.
 */
static errval_t cap_revoke_remote(struct capref root, capaddr_t src, uint8_t level)
{
    return LIB_ERR_NOT_IMPLEMENTED;
}

/**
 * \brief Retype (part of) a capability into one or more new capabilities
 *
 * \param dest_start    Location of first destination slot, which must be empty
 * \param src           Source capability to retype
 * \param offset        Offset into source capability
 * \param new_type      Kernel object type to retype to.
 * \param objsize       Size of created objects in bytes
 *                      (ignored for fixed-size objects)
 * \param count         The number of new objects to create
 *
 * When retyping IRQSrc capabilities, offset and objsize represent the start
 * and end of the to be created interrupt range. Count must be 1 for IRQSrc.
 *
 * Retypes (part of) the given source capability into a number of new
 * capabilities, which may be of the same or of different type. The new
 * capabilities are created in the slots starting from dest_start, which must
 * all be empty and lie in the same CNode. The number of objects created is
 * determined by the argument `count`.
 */
errval_t cap_retype(struct capref dest_start, struct capref src, gensize_t offset,
                    enum objtype new_type, gensize_t objsize, size_t count)
{
    errval_t err;

    // Address of destination cspace
    capaddr_t dcs_addr = get_croot_addr(dest_start);
    // Address of the cap to the destination CNode
    capaddr_t dcn_addr = get_cnode_addr(dest_start);
    // Depth/Level of destination cnode
    enum cnode_type dcn_level = get_cnode_level(dest_start);
    // Address of source cspace
    capaddr_t scp_root = get_croot_addr(src);
    // Address of source capability
    capaddr_t scp_addr = get_cap_addr(src);

    err = invoke_cnode_retype(cap_root, scp_root, scp_addr, offset, new_type,
                              objsize, count, dcs_addr, dcn_addr, dcn_level,
                              dest_start.slot);

    if (err_no(err) == SYS_ERR_RETRY_THROUGH_MONITOR) {
        struct capref src_root = get_croot_capref(src);
        struct capref dest_root = get_croot_capref(dest_start);
        return cap_retype_remote(src_root, dest_root, scp_addr, offset, new_type,
                                 objsize, count, dcn_addr, dest_start.slot,
                                 dcn_level);
    } else {
        return err;
    }
}


/**
 * \brief Create a capability
 *
 * \param dest      Location where to create the cap, which must be empty.
 * \param type      Kernel object type to create.
 * \param size      Size of the created capability in bytes.
 *                  (ignored for fixed-size objects)
 *
 * Only certain types of capabilities can be created this way. If invoked on
 * a capability type, that is not creatable at runtime the error
 * SYS_ERR_TYPE_NOT_CREATABLE is returned. Most capabilities have to be retyped
 * from other capabilities with cap_retype().
 */
errval_t cap_create(struct capref dest, enum objtype type, size_t size)
{
    errval_t err;

    // Address of the cap to the destination CNode
    capaddr_t dest_cnode_cptr = get_cnode_addr(dest);
    enum cnode_type dest_cnode_level = get_cnode_level(dest);

    err = invoke_cnode_create(cap_root, type, size, dest_cnode_cptr,
                              dest_cnode_level, dest.slot);

    return err;
}

/**
 * \brief Delete the given capability
 *
 * \param cap Capability to be deleted
 *
 * Deletes (but does not revoke) the given capability, allowing the CNode slot
 * to be reused.
 */
errval_t cap_delete(struct capref cap)
{
    errval_t err;
    struct capref croot = get_croot_capref(cap);
    capaddr_t caddr = get_cap_addr(cap);
    enum cnode_type level = get_cap_level(cap);

    err = invoke_cnode_delete(croot, caddr, level);

    if (err_no(err) == SYS_ERR_RETRY_THROUGH_MONITOR) {
        return cap_delete_remote(croot, caddr, level);
    } else {
        return err;
    }
}

/**
 * \brief Revoke (delete all copies and descendants of) the given capability
 *
 * \param cap Capability to be revoked
 *
 * Deletes all copies and descendants of the given capability, but not the
 * capability itself. If this succeeds, the capability is guaranteed to be
 * the only copy in the system.
 */
errval_t cap_revoke(struct capref cap)
{
    errval_t err;
    struct capref croot = get_croot_capref(cap);
    capaddr_t caddr = get_cap_addr(cap);
    enum cnode_type level = get_cap_level(cap);

    err = invoke_cnode_revoke(croot, caddr, level);

    if (err_no(err) == SYS_ERR_RETRY_THROUGH_MONITOR) {
        return cap_revoke_remote(croot, caddr, level);
    } else {
        return err;
    }
}

/**
 * \brief Destroy a capability, i.e. delete it and free the slot.
 *
 * \param cap           Capability to be destroyed
 */
errval_t cap_destroy(struct capref cap)
{
    errval_t err;
    err = cap_delete(cap);
    if (err_is_fail(err)) {
        return err;
    }

    err = slot_free(cap);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_WHILE_FREEING_SLOT);
    }

    return SYS_ERR_OK;
}

/**
 * \brief Replace own L1 CNode
 *
 * \param new the replacement L1 CNode
 * \param ret the slot to put the old L1 CNode
 */
errval_t root_cnode_resize(struct capref new, struct capref ret)
{
    assert(get_croot_addr(new) == CPTR_ROOTCN);
    assert(get_cap_level(new) == CNODE_TYPE_COUNT);
    capaddr_t new_cptr = get_cap_addr(new);

    assert(get_croot_addr(ret) == CPTR_ROOTCN);
    assert(get_cap_level(ret) == CNODE_TYPE_COUNT);
    capaddr_t retcn_ptr= get_cnode_addr(ret);

    return invoke_cnode_resize(cap_root, new_cptr, retcn_ptr, ret.slot);
}

/**
 * \brief Create a CNode from a given RAM capability in a specific slot
 *
 * \param dest location in which to place newly-created CNode cap
 * \param src  location of RAM capability to be retyped to new CNode
 * \param cnoderef cnoderef struct, filled-in if non-NULL with relevant info
 * \param slots number of slots in created CNode
 *                  must match size of RAM capability.
 *
 * This function requires that dest refer to an existing but empty slot. It
 * retypes the given memory to a new CNode.
 */
errval_t cnode_create_from_mem(struct capref dest, struct capref src,
                               enum objtype cntype, struct cnoderef *cnoderef,
                               size_t slots)
{
    errval_t err;

    if (cntype != ObjType_L1CNode &&
        cntype != ObjType_L2CNode)
    {
        return LIB_ERR_CNODE_TYPE;
    }


    // Retype it to the destination
    err = cap_retype(dest, src, 0, cntype, slots * OBJSIZE_CTE, 1);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CAP_RETYPE);
    }

    // Construct the cnoderef to return
    if (cnoderef != NULL) {
        enum cnode_type ref_cntype = cntype == ObjType_L1CNode ? CNODE_TYPE_ROOT : CNODE_TYPE_OTHER;
        *cnoderef = build_cnoderef(dest, ref_cntype);
    }

    return SYS_ERR_OK;
}

/**
 * \brief Create a CNode from newly-allocated RAM in a newly-allocated slot
 *
 * \param ret_dest capref struct to be filled-in with location of CNode
 * \param cnoderef cnoderef struct, filled-in if non-NULL with relevant info
 * \param slots Minimum number of slots in created CNode
 * \param retslots If non-NULL, filled in with the  number of slots in created CNode
 */
errval_t cnode_create(struct capref *ret_dest, struct cnoderef *cnoderef,
                      cslot_t slots, cslot_t *retslots)
{
    USER_PANIC("cnode_create deprecated; use cnode_create_l1, cnode_create_l2, or cnode_create_foreign_l2: %p %p %p %p\n",
            __builtin_return_address(0),
#ifdef __x86_64__
            __builtin_return_address(1),
            __builtin_return_address(2),
            __builtin_return_address(3)
#else
            NULL, NULL, NULL
#endif
            );
    return LIB_ERR_NOT_IMPLEMENTED;
}

/**
 * \brief Create a L2 CNode from newly-allocated RAM in a newly-allocated slot
 *
 * \param ret_dest capref struct to be filled-in with location of CNode
 * \param cnoderef cnoderef struct, filled-in if non-NULL with relevant info
 *
 * This function always creates a L2 CNode which contains 256 capabilities
 */
errval_t cnode_create_l2(struct capref *ret_dest, struct cnoderef *cnoderef)
{
    errval_t err;

    // Allocate a slot in root cn for destination
    assert(ret_dest != NULL);
    err = slot_alloc_root(ret_dest);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_SLOT_ALLOC);
    }

    cslot_t retslots;
    err = cnode_create_raw(*ret_dest, cnoderef, ObjType_L2CNode,
                           L2_CNODE_SLOTS, &retslots);
    if (retslots != L2_CNODE_SLOTS) {
        debug_printf("Unable to create properly sized L2 CNode: got %"PRIuCSLOT" slots instead of %"PRIuCSLOT"\n",
                retslots, (cslot_t)L2_CNODE_SLOTS);
    }
    return err;
}

errval_t cnode_create_l1(struct capref *ret_dest, struct cnoderef *cnoderef)
{
    errval_t err;

    // Allocate a slot in root cn for destination
    assert(ret_dest != NULL);
    err = slot_alloc(ret_dest);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_SLOT_ALLOC);
    }

    cslot_t retslots;
    err = cnode_create_raw(*ret_dest, cnoderef, ObjType_L1CNode,
                           L2_CNODE_SLOTS, &retslots);
    if (retslots != L2_CNODE_SLOTS) {
        debug_printf("Unable to create initial L1 CNode: got %"PRIuCSLOT" slots instead of %"PRIuCSLOT"\n",
                retslots, (cslot_t)L2_CNODE_SLOTS);
    }
    return err;
}

/**
 * \brief Create a CNode for another cspace from newly-allocated RAM in a
 *        newly-allocated slot
 *
 * \param dest_l1   capref to L1 (root) cnode of destination cspace
 * \param dest_slot slot to fill with new cnode in destination L1 cnode
 * \param cnoderef  cnoderef struct, filled-in if non-NULL with relevant info
 *
 * This function creates a CNode which contains 256 capabilities initially
 * and puts it in a slot in our cspace.
 */
errval_t cnode_create_foreign_l2(struct capref dest_l1, cslot_t dest_slot,
                                 struct cnoderef *cnoderef)
{
    errval_t err;

    if (capref_is_null(dest_l1)) {
        return LIB_ERR_CROOT_NULL;
    }
    assert(!capref_is_null(dest_l1));

    struct capref dest;
    dest.cnode = build_cnoderef(dest_l1, CNODE_TYPE_ROOT);
    dest.slot = dest_slot;

    cslot_t retslots;
    err = cnode_create_raw(dest, NULL, ObjType_L2CNode, L2_CNODE_SLOTS, &retslots);
    if (retslots != L2_CNODE_SLOTS) {
        debug_printf("Unable to create properly sized foreign CNode: "
                     "got %"PRIuCSLOT" slots instead of %"PRIuCSLOT"\n",
                     retslots, (cslot_t)L2_CNODE_SLOTS);
    }

    // Create proper cnoderef for foreign L2
    if (cnoderef) {
        cnoderef->croot = get_cap_addr(dest_l1);
        cnoderef->cnode = ROOTCN_SLOT_ADDR(dest_slot);
        cnoderef->level = CNODE_TYPE_OTHER;
    }
    return err;
}

/**
 * \brief Create a CNode from newly-allocated RAM in the given slot
 *
 * \param dest location in which to place CNode cap
 * \param cnoderef cnoderef struct, filled-in if non-NULL with relevant info
 * \param cntype, type of new cnode
 * \param slots Minimum number of slots in created CNode
 * \param retslots If non-NULL, filled in with the  number of slots in created CNode
 *
 * This function requires that dest refer to an existing but empty slot. It
 * allocates memory (using #ram_alloc), and retypes that memory to a new CNode.
 * The intermediate ram cap is destroyed.
 */
errval_t cnode_create_raw(struct capref dest, struct cnoderef *cnoderef,
                          enum objtype cntype, cslot_t slots, cslot_t *retslots)
{
    errval_t err;
    struct capref ram;

    assert(slots > 0);

    if (cntype != ObjType_L1CNode &&
        cntype != ObjType_L2CNode)
    {
        return LIB_ERR_CNODE_TYPE;
    }

    if (slots < L2_CNODE_SLOTS ||
        (cntype == ObjType_L2CNode && slots != L2_CNODE_SLOTS))
    {
        return LIB_ERR_CNODE_SLOTS;
    }

    if (retslots != NULL) {
        *retslots = slots;
    }

    // Allocate some memory
    err = ram_alloc(&ram, slots * OBJSIZE_CTE);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_RAM_ALLOC);
    }

    err = cnode_create_from_mem(dest, ram, cntype, cnoderef, slots);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CNODE_CREATE_FROM_MEM);
    }

    err = cap_destroy(ram);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CAP_DESTROY);
    }

    return SYS_ERR_OK;
}

/**
 * \brief Create CNode with a given guard
 *
 * \param dest         Location where to place the cnode
 * \param cnoderef   Filled in cnoderef struct if non-NULL
 * \param slots Minimum number of slots in created CNode
 * \param retslots If non-NULL, filled in with the  number of slots in created CNode
 * \param guard        The guard value to set
 * \param guard_size   The length of the guard in bits
 *
 * This function requires that dest refer to an existing but empty slot. It
 * allocates memory (using #ram_alloc), and retypes that memory to a new CNode
 * with the given guard value and size. An intermediate slot is used in order to
 * set the guard value.
 */
errval_t cnode_create_with_guard(struct capref dest, struct cnoderef *cnoderef,
                                 cslot_t slots, cslot_t *retslots,
                                 uint64_t guard, uint8_t guard_size)
{
    USER_PANIC("%s: GPT CNodes are deprecated\n", __FUNCTION__);
}

/**
 * \brief Create a VNode in newly-allocated memory
 *
 * \param dest location to place new VNode cap
 * \param type VNode type to create
 *
 * This function requires that dest refer to an existing but empty slot.
 * The intermidiate ram cap is destroyed.
 */
errval_t vnode_create(struct capref dest, enum objtype type)
{
    errval_t err;

    struct capref ram;

    err = ram_alloc_aligned(&ram, vnode_objsize(type), vnode_objsize(type));
    if (err_no(err) == LIB_ERR_RAM_ALLOC_WRONG_SIZE && type != ObjType_VNode_ARM_l1) {
        // can only get 4kB pages, cannot create ARM_l1, and waste 3kB for
        // ARM_l2
        err = ram_alloc(&ram, BASE_PAGE_SIZE);
    }
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_RAM_ALLOC);
    }

    assert(type_is_vnode(type));
    err = cap_retype(dest, ram, 0, type, vnode_objsize(type), 1);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CAP_RETYPE);
    }

    err = cap_destroy(ram);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CAP_DESTROY);
    }

    return SYS_ERR_OK;
}

/**
 * \brief Create a Frame cap referring to newly-allocated RAM in a given slot
 *
 * \param dest  Location to place new frame cap
 * \param bytes Minimum size of frame to create
 * \param retbytes If non-NULL, filled in with size of created frame
 *
 * This function requires that dest refer to an existing but empty slot.
 * #ram_alloc is used to allocate memory. After retyping the intermediate
 * ram cap is destroyed.
 *
 * This function will returns a special error code if ram_alloc fails
 * due to the constrains on the memory server (size of cap or region
 * of memory). This is to facilitate retrying with different
 * constraints.
 */
errval_t frame_create(struct capref dest, size_t bytes, size_t *retbytes)
{
    DEBUG_BEGIN;
    assert(bytes > 0);
    errval_t err;

    bytes = ROUND_UP(bytes, BASE_PAGE_SIZE);

    struct capref ram;
    err = ram_alloc(&ram, bytes);
    if (err_is_fail(err)) {
        if (err_no(err) == MM_ERR_NOT_FOUND ||
            err_no(err) == LIB_ERR_RAM_ALLOC_WRONG_SIZE) {
            return err_push(err, LIB_ERR_RAM_ALLOC_MS_CONSTRAINTS);
        }
        return err_push(err, LIB_ERR_RAM_ALLOC);
    }
    err = cap_retype(dest, ram, 0, ObjType_Frame, bytes, 1);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CAP_RETYPE);
    }

    err = cap_destroy(ram);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CAP_DESTROY);
    }

    if (retbytes != NULL) {
        *retbytes = bytes;
    }

    return SYS_ERR_OK;
}

/**
 * \brief Create a dispatcher capability and store it in the slot pointed to
 * by 'dest'
 *
 * This function requires that dest refers to an existing but empty slot. It
 * allocates a new RAM cap, retypes it to one of type ObjType_Dispatcher and
 * stores it at the slot pointed to by the capref struct 'dest'. The intermediate
 * ram cap is then destroyed.
 *
 * \param dest location to place new dispatcher cap
 *
 * \return Either SYS_ERR_OK if no error occured or an error
 * indicating what went wrong otherwise.
 */
errval_t dispatcher_create(struct capref dest)
{
    errval_t err;

    struct capref ram;
    assert(1 << log2ceil(OBJSIZE_DISPATCHER) == OBJSIZE_DISPATCHER);
    size_t size = OBJSIZE_DISPATCHER;
    err = ram_alloc(&ram, size);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_RAM_ALLOC);
    }

    err = cap_retype(dest, ram, 0, ObjType_Dispatcher, 0, 1);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CAP_RETYPE);
    }

    err = cap_destroy(ram);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CAP_DESTROY);
    }
    return SYS_ERR_OK;
}

/**
 * \brief Create endpoint to caller on current dispatcher.
 *
 * \param buflen  Length of incoming LMP buffer, in words
 * \param retcap  Pointer to capref struct, filled-in with location of cap
 * \param retep   Double pointer to LMP endpoint, filled-in with allocated EP
 */
errval_t endpoint_create(size_t buflen, struct capref *retcap,
                         struct lmp_endpoint **retep)
{
    errval_t err = slot_alloc(retcap);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_SLOT_ALLOC);
    }

    return lmp_endpoint_create_in_slot(buflen, *retcap, retep);
}

/**
 * \brief Create a Frame cap referring to newly-allocated RAM in an allocated slot
 *
 * \param dest  Pointer to capref struct, filled-in with location of new cap
 * \param bytes Minimum size of frame to create
 * \param retbytes If non-NULL, filled in with size of created frame
 */
errval_t frame_alloc(struct capref *dest, size_t bytes, size_t *retbytes)
{
    errval_t err = slot_alloc(dest);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_SLOT_ALLOC);
    }

    return frame_create(*dest, bytes, retbytes);
}

/**
 * \brief Create a DevFrame cap by retyping out of given source PhysAddr cap
 *
 * \param dest          Pointer to capref struct, filled-in with location of new cap
 * \param src           Cap_info struct for the source PhysAddr cap
 * \param size_bits     Size of created objects as a power of two
 *                      (ignored for fixed-size objects)
 */
errval_t devframe_type(struct capref *dest, struct capref src, uint8_t bits)
{
    errval_t err = slot_alloc(dest);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_SLOT_ALLOC);
    }

    return cap_retype(*dest, src, 0, ObjType_DevFrame, 1UL << bits, 1);
}

/**
 * \brief Create an ID cap in a newly allocated slot.
 *
 * \param dest  Pointer to capref struct, filld-in with location of new cap.
 *
 * The caller is responsible for revoking the cap after using it.
 */
errval_t idcap_alloc(struct capref *dest)
{
    errval_t err = slot_alloc(dest);

    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_SLOT_ALLOC);
    }

    return idcap_create(*dest);
}

/**
 * \brief Create an ID cap in the specified slot.
 *
 * \param dest  Capref, where ID cap should be created.
 *
 * The caller is responsible for revoking the cap after using it.
 */
errval_t idcap_create(struct capref dest)
{
    return cap_create(dest, ObjType_ID, 0);
}

/**
 * \brief Builds a #cnoderef struct from a #capref struct using cap
 *        identification.
 *
 * \param cnoder Pointer to a cnoderef struct, fill-in by function.
 * \param capr   Capref to a CNode capability.
 */
errval_t cnode_build_cnoderef(struct cnoderef *cnoder, struct capref capr)
{
    struct capability cap;
    errval_t err = debug_cap_identify(capr, &cap);
    if (err_is_fail(err)) {
        return err;
    }

    if (cap.type != ObjType_L1CNode &&
        cap.type != ObjType_L2CNode) {
        return LIB_ERR_NOT_CNODE;
    }

    if (!cnodecmp(capr.cnode, cnode_root)) {
        USER_PANIC("cnode_build_cnoderef NYI for non rootcn caprefs");
    }

    cnoder->croot = get_croot_addr(capr);
    cnoder->cnode = capr.slot << L2_CNODE_BITS;
    cnoder->level = CNODE_TYPE_OTHER;

    return SYS_ERR_OK;
}
