Beispiel #1
0
static void run_benchmark(coreid_t core, int requests)
{
    errval_t err;
    struct capref ramcap;
    
    int i = -1;
    int bits = MEM_BITS;

    debug_printf("starting benchmark. allocating mem of size: %d\n", bits);
    //debug_printf("starting benchmark. allocating mem of size: %d to %d\n", 
    //             MINSIZEBITS, MINSIZEBITS+requests-1);


    sleep_init();

    do {
        i++;
        // bits =  MINSIZEBITS+i;
        trace_event(TRACE_SUBSYS_MEMTEST, TRACE_EVENT_MEMTEST_ALLOC, i);
        err = ram_alloc(&ramcap, bits);
        // milli_sleep(1);

        /*
        if ((i % 500 == 0) && (i > 0)) {
            debug_printf("allocated %d caps\n", i);
        }
        */

    } while (err_is_ok(err)); // && (i < requests));

    debug_printf("done benchmark. allocated %d caps (%lu bytes)\n", 
                 i, i * (1UL << bits));

}
Beispiel #2
0
int main(int argc, char **argv)
{
    struct capref theram;

    // Get an 8K RAM cap
    errval_t err = ram_alloc(&theram, 13);
    assert(err_is_ok(err));

    for(int i = 0; i < 100; i++) {
        thread_yield();
    }

    struct capability cap;
    err = debug_cap_identify(theram, &cap);
    assert(err_is_ok(err));
    assert(cap.type == ObjType_RAM);
    printf("got RAM at 0x%" PRIxGENPADDR ", size %u\n",
           cap.u.ram.base, cap.u.ram.bits);

    struct capref leftcap, rightcap;

    // XXX: Hopefully allocates two consecutive slots
    err = slot_alloc(&leftcap);
    assert(err_is_ok(err));
    err = slot_alloc(&rightcap);
    assert(err_is_ok(err));

    // Split in half
    err = cap_retype(leftcap, theram, ObjType_RAM, 12);
    assert(err_is_ok(err));

    err = debug_cap_identify(leftcap, &cap);
    assert(err_is_ok(err));
    assert(cap.type == ObjType_RAM);
    printf("left half at 0x%" PRIxGENPADDR ", size %u\n",
           cap.u.ram.base, cap.u.ram.bits);

    err = debug_cap_identify(rightcap, &cap);
    assert(err_is_ok(err));
    assert(cap.type == ObjType_RAM);
    printf("right half at 0x%" PRIxGENPADDR ", size %u\n",
           cap.u.ram.base, cap.u.ram.bits);

    // Delete the parent (8K range)
    printf("deleting parent\n");
    err = cap_delete(theram);
    assert(err_is_ok(err));

    // Delete the left half (4K)
    printf("deleting left half\n");
    err = cap_delete(leftcap);
    assert(err_is_ok(err));

    // Delete the right half (4K)
    printf("deleting right half\n");
    err = cap_delete(rightcap);
    assert(err_is_ok(err));

    return 0;
}
Beispiel #3
0
void rom_setvramsize(rom_t *r,int banks)
{
	banks *= 0x2000;

	//if vram size hasnt changed, return
	if(r->vramsize == banks)
		return;

	//set vram size and create mask
	r->vramsize = banks;
	r->vrammask = rom_createmask(banks);

	//allocate (or reallocate) memory
	r->vram = ram_alloc(r->vramsize,r->vram);
	r->vcache = (cache_t*)ram_alloc(r->vramsize,r->vcache);
	r->vcache_hflip = (cache_t*)ram_alloc(r->vramsize,r->vcache_hflip);

	log_message("vram size set to %dk\n",banks);
}
static void ram_hack(void)
{
    errval_t err;

    for (int i = 0; i < 100; i++) {
        err = ram_alloc(&ram_caps[i], BASE_PAGE_BITS);
        assert(err_is_ok(err));
    }

    err = ram_alloc_set(my_ram_alloc);
    assert(err_is_ok(err));
}
Beispiel #5
0
void rom_setwramsize(rom_t *r,int banks)
{
	banks *= 0x1000;

	//if wram size hasnt changed, return
	if(r->wramsize == banks)
		return;

	//set wram size and create mask
	r->wramsize = banks;
	r->wrammask = rom_createmask(banks);

	//allocate (or reallocate) memory
	r->wram = ram_alloc(r->wramsize,r->wram);

	log_message("wram size set to %dk\n",banks);
}
static void create_caps(void) 
{
    errval_t err;

    // allocate a bunch of ramcaps
    for (int i=0; i<CAPS_PER_CORE; i++) {
        err = ram_alloc(&my_caps[i], CHILD_BITS);
        if (err_is_fail(err)) {
            DEBUG_ERR(err, "xcorecap: RAM alloc failed\n");   
            abort(); 
        }
        err = slot_alloc(&retyped_caps[i]);
        if (err_is_fail(err)) {
            DEBUG_ERR(err, "slot alloc err\n");   
            abort(); 
        }
    }
    printf("core %i caps created\n", my_coreid);
}
Beispiel #7
0
static void run_benchmark(coreid_t core)
{
    errval_t err;
    struct capref ramcap;

    debug_printf("starting benchmark. allocating mem of size: %d\n", MEM_BITS);
    
    int i = -1;

    do {
        i++;
        err = ram_alloc(&ramcap, MEM_BITS);
    } while (err_is_ok(err));

    debug_printf("done benchmark. allocated %d caps (%lu bytes)\n", 
                 i, i * (1UL << MEM_BITS));

    ns_barrier_register_n(core, "mem_bench");
}
Beispiel #8
0
static void run_benchmark_0(coreid_t core)
{
    assert(core == 0);

    errval_t err;
    struct capref ramcap;

    debug_printf("starting benchmark. allocating mem of size: %d\n", MEM_BITS);
    
    int i = -1;

    do {
        i++;
        err = ram_alloc(&ramcap, MEM_BITS);
    } while (err_is_ok(err) && (i < 10));

    debug_printf("done benchmark. allocated %d caps (%lu bytes)\n", 
                 i, i * (1UL << MEM_BITS));
}
Beispiel #9
0
static void run_benchmark(coreid_t core, int requests)
{
    errval_t err;
    struct capref ramcap;

    
    int i = -1;
    int bits = MEM_BITS;

    debug_printf("starting benchmark. allocating mem of size: %d\n", bits);
    //debug_printf("starting benchmark. allocating mem of size: %d to %d\n", 
    //             MINSIZEBITS, MINSIZEBITS+requests-1);


    sleep_init();

    // NOTE: because this is allocating and freeing, it should never stop.
    // it should also never have to steal

    do {
        i++;
        // bits =  MINSIZEBITS+i;
        trace_event(TRACE_SUBSYS_MEMTEST, TRACE_EVENT_MEMTEST_ALLOC, i);
        err = ram_alloc(&ramcap, bits);
        if (err_is_fail(err)) {
            DEBUG_ERR(err, "ram_alloc failed");
        }
        milli_sleep(1);
        err = ram_free(ramcap);
        if (err_is_fail(err)) {
            DEBUG_ERR(err, "ram_free failed");
        }
        if ((i % 500 == 0) && (i > 0)) {
            debug_printf("allocated %d caps\n", i);
        }
    } while (err_is_ok(err)); // && (i < requests));

    debug_printf("done benchmark. allocated %d caps (%lu bytes)\n", 
                 i, i * (1UL << bits));

}
Beispiel #10
0
int map_unmap(void)
{
    errval_t err;
    struct capref mem;

    DEBUG_MAP_UNMAP("ram_alloc\n");
    err = ram_alloc(&mem, BASE_PAGE_BITS);
    if (err_is_fail(err)) {
        printf("ram_alloc: %s (%"PRIuERRV")\n", err_getstring(err), err);
        return 1;
    }

    struct capref frame;
    DEBUG_MAP_UNMAP("retype\n");
    err = slot_alloc(&frame);
    if (err_is_fail(err)) {
        printf("slot_alloc: %s (%"PRIuERRV")\n", err_getstring(err), err);
        return 1;
    }
    err = cap_retype(frame, mem, ObjType_Frame, BASE_PAGE_BITS);
    if (err_is_fail(err)) {
        printf("cap_retype: %s (%"PRIuERRV")\n", err_getstring(err), err);
        return 1;
    }

    DEBUG_MAP_UNMAP("delete ram cap\n");
    err = cap_destroy(mem);
    if (err_is_fail(err)) {
        printf("cap_delete(mem): %s (%"PRIuERRV")\n", err_getstring(err), err);
        return 1;
    }

    struct frame_identity fi;
    err = invoke_frame_identify(frame, &fi);
    if (err_is_fail(err)) {
        printf("frame_identify: %s (%"PRIuERRV")\n", err_getstring(err), err);
        return 1;
    }
    DEBUG_MAP_UNMAP("frame: base = 0x%"PRIxGENPADDR", bits = %d\n", fi.base, fi.bits);

#ifdef NKMTEST_DEBUG_MAP_UNMAP
    dump_pmap(get_current_pmap());
#endif

    struct vregion *vr;
    struct memobj *memobj;
    void *vaddr;
    DEBUG_MAP_UNMAP("map\n");
    err = vspace_map_one_frame(&vaddr, BASE_PAGE_SIZE, frame, &memobj, &vr);
    if (err_is_fail(err)) {
        printf("vspace_map_one_frame: %s (%"PRIuERRV")\n", err_getstring(err), err);
    }
    char *memory = vaddr;
    DEBUG_MAP_UNMAP("vaddr = %p\n", vaddr);

#ifdef NKMTEST_DEBUG_MAP_UNMAP
    dump_pmap(get_current_pmap());
#endif

    DEBUG_MAP_UNMAP("write 1\n");
    int i;
    for (i = 0; i < BASE_PAGE_SIZE; i++) {
        memory[i] = i % INT8_MAX;
    }
    DEBUG_MAP_UNMAP("verify 1\n");
    for (i = 0; i < BASE_PAGE_SIZE; i++) {
        assert(memory[i] == i % INT8_MAX);
    }

    DEBUG_MAP_UNMAP("delete frame cap\n");
    err = cap_destroy(frame);
    if (err_is_fail(err)) {
        printf("cap_delete(frame): %s (%"PRIuERRV")\n", err_getstring(err), err);
        return 1;
    }

#ifdef NKMTEST_DEBUG_MAP_UNMAP
    // no mapping should remain here
    dump_pmap(get_current_pmap());
    err = debug_dump_hw_ptables();
    if (err_is_fail(err)) {
        printf("kernel dump ptables: %s (%"PRIuERRV")\n", err_getstring(err), err);
        return 1;
    }
#endif

    printf("%s: done\n", __FUNCTION__);
    return 0;
}
Beispiel #11
0
/**
 * \brief Setup an initial cspace
 *
 * Create an initial cspace layout
 */
static errval_t spawn_setup_cspace(struct spawninfo *si)
{
    errval_t err;
    struct capref t1;

    /* Create root CNode */
    err = cnode_create(&si->rootcn_cap, &si->rootcn, DEFAULT_CNODE_SLOTS, NULL);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_CREATE_ROOTCN);
    }

    /* Create taskcn */
    err = cnode_create(&si->taskcn_cap, &si->taskcn, DEFAULT_CNODE_SLOTS, NULL);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_CREATE_TASKCN);
    }

    // Mint into rootcn setting the guard
    t1.cnode = si->rootcn;
    t1.slot  = ROOTCN_SLOT_TASKCN;
    err = cap_mint(t1, si->taskcn_cap, 0,
                   GUARD_REMAINDER(2 * DEFAULT_CNODE_BITS));
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_MINT_TASKCN);
    }

    /* Create slot_alloc_cnode */
    t1.cnode = si->rootcn;
    t1.slot  = ROOTCN_SLOT_SLOT_ALLOC0;
    err = cnode_create_raw(t1, NULL, (1<<SLOT_ALLOC_CNODE_BITS), NULL);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_CREATE_SLOTALLOC_CNODE);
    }
    t1.cnode = si->rootcn;
    t1.slot  = ROOTCN_SLOT_SLOT_ALLOC1;
    err = cnode_create_raw(t1, NULL, (1<<SLOT_ALLOC_CNODE_BITS), NULL);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_CREATE_SLOTALLOC_CNODE);
    }
    t1.cnode = si->rootcn;
    t1.slot  = ROOTCN_SLOT_SLOT_ALLOC2;
    err = cnode_create_raw(t1, NULL, (1<<SLOT_ALLOC_CNODE_BITS), NULL);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_CREATE_SLOTALLOC_CNODE);
    }

    // Create DCB
    si->dcb.cnode = si->taskcn;
    si->dcb.slot  = TASKCN_SLOT_DISPATCHER;
    err = dispatcher_create(si->dcb);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_CREATE_DISPATCHER);
    }

    // Give domain endpoint to itself (in taskcn)
    struct capref selfep = {
        .cnode = si->taskcn,
        .slot = TASKCN_SLOT_SELFEP,
    };
    err = cap_retype(selfep, si->dcb, ObjType_EndPoint, 0);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_CREATE_SELFEP);
    }

    // Map root CNode (in taskcn)
    t1.cnode = si->taskcn;
    t1.slot  = TASKCN_SLOT_ROOTCN;
    err = cap_mint(t1, si->rootcn_cap, 0, 0);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_MINT_ROOTCN);
    }

#ifdef TRACING_EXISTS
    // Set up tracing for the child
    err = trace_setup_child(si->taskcn, si->handle);
    if (err_is_fail(err)) {
        printf("Warning: error setting up tracing for child domain\n");
        // SYS_DEBUG(err, ...);
    }
#endif

    // XXX: copy over argspg?
    memset(&si->argspg, 0, sizeof(si->argspg));

    /* Fill up basecn */
    struct capref   basecn_cap;
    struct cnoderef basecn;

    // Create basecn in rootcn
    basecn_cap.cnode = si->rootcn;
    basecn_cap.slot  = ROOTCN_SLOT_BASE_PAGE_CN;
    err = cnode_create_raw(basecn_cap, &basecn, DEFAULT_CNODE_SLOTS, NULL);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_CNODE_CREATE);
    }

    // Place the ram caps
    for (uint8_t i = 0; i < DEFAULT_CNODE_SLOTS; i++) {
        struct capref base = {
            .cnode = basecn,
            .slot  = i
        };
        struct capref ram;
        err = ram_alloc(&ram, BASE_PAGE_BITS);
        if (err_is_fail(err)) {
            return err_push(err, LIB_ERR_RAM_ALLOC);
        }
        err = cap_copy(base, ram);

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

    return SYS_ERR_OK;
}

static errval_t spawn_setup_vspace(struct spawninfo *si)
{
    errval_t err;

    /* Create pagecn */
    si->pagecn_cap = (struct capref){.cnode = si->rootcn, .slot = ROOTCN_SLOT_PAGECN};
    err = cnode_create_raw(si->pagecn_cap, &si->pagecn, PAGE_CNODE_SLOTS, NULL);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_CREATE_PAGECN);
    }

    /* Init pagecn's slot allocator */

    // XXX: satisfy a peculiarity of the single_slot_alloc_init_raw API
    size_t bufsize = SINGLE_SLOT_ALLOC_BUFLEN(PAGE_CNODE_SLOTS);
    void *buf = malloc(bufsize);
    assert(buf != NULL);

    err = single_slot_alloc_init_raw(&si->pagecn_slot_alloc, si->pagecn_cap,
                                     si->pagecn, PAGE_CNODE_SLOTS,
                                     buf, bufsize);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_SINGLE_SLOT_ALLOC_INIT_RAW);
    }

    // Create root of pagetable
    err = si->pagecn_slot_alloc.a.alloc(&si->pagecn_slot_alloc.a, &si->vtree);
    if (err_is_fail(err)) {
        return err_push(err, LIB_ERR_SLOT_ALLOC);
    }

    // top-level table should always live in slot 0 of pagecn
    assert(si->vtree.slot == 0);

    switch(si->cpu_type) {
    case CPU_X86_64:
    case CPU_K1OM:
        err = vnode_create(si->vtree, ObjType_VNode_x86_64_pml4);
        break;

    case CPU_X86_32:
    case CPU_SCC:
#ifdef CONFIG_PAE
        err = vnode_create(si->vtree, ObjType_VNode_x86_32_pdpt);
#else
        err = vnode_create(si->vtree, ObjType_VNode_x86_32_pdir);
#endif
        break;

    case CPU_ARM5:
    case CPU_ARM7:
        err = vnode_create(si->vtree, ObjType_VNode_ARM_l1);
        break;

    default:
        assert(!"Other architecture");
        return err_push(err, SPAWN_ERR_UNKNOWN_TARGET_ARCH);
    }

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

    err = spawn_vspace_init(si, si->vtree, si->cpu_type);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_VSPACE_INIT);
    }

    return SYS_ERR_OK;
}

#if 0
/**
 * \brief Lookup and map an image
 */
static errval_t spawn_map(const char *name, struct bootinfo *bi,
                          lvaddr_t *binary, size_t *binary_size)
{
    errval_t err;

    /* Get the module from the multiboot */
    struct mem_region *module = multiboot_find_module(bi, name);
    if (module == NULL) {
        return SPAWN_ERR_FIND_MODULE;
    }

    /* Map the image */
    err = spawn_map_module(module, binary_size, binary, NULL);
    if (err_is_fail(err)) {
        return err_push(err, SPAWN_ERR_MAP_MODULE);
    }

    return SYS_ERR_OK;
}
int invalid_mappings(void)
{
    // outline:
    // get pml4, pdpt, pdir, ptable, and frame
    // check all combinations to make sure that restrictions are implemented
    // correctly in kernel space
    // VALID:
    // map pdpt in pml4
    // map pdir in pdpt
    // map pt   in pdir
    // map frame in {pt, pdir, pdpt}
    // INVALID:
    // all other combinations

    errval_t err;
    struct capref caps[7];
    struct capref mapping;

    // allocate slot for mapping cap: can reuse`
    err = slot_alloc(&mapping);
    if (err_is_fail(err)) {
        debug_printf("slot_alloc: %s (%ld)\n", err_getstring(err), err);
        return 1;
    }

    // allocate caps
    for (int i = 0; i < 5; i++) {
        // get 4k block
        struct capref mem;
        err = ram_alloc(&mem, BASE_PAGE_BITS);
        if (err_is_fail(err)) {
            debug_printf("ram_alloc: %s (%ld)\n", err_getstring(err), err);
            return 1;
        }

        // get slot for retype dest
        err = slot_alloc(&caps[i]);
        if (err_is_fail(err)) {
            debug_printf("slot_alloc: %s (%ld)\n", err_getstring(err), err);
            return 1;
        }
        // retype to selected type
        err = cap_retype(caps[i], mem, types[i], BASE_PAGE_BITS);
        if (err_is_fail(err)) {
            debug_printf("cap_retype: %s (%ld)\n", err_getstring(err), err);
            return 1;
        }

        // cleanup source cap
        DEBUG_INVALID_MAPPINGS("delete ram cap\n");
        err = cap_destroy(mem);
        if (err_is_fail(err)) {
            debug_printf("cap_delete(mem): %s (%ld)\n", err_getstring(err), err);
            return 1;
        }
    }
    // cap 6: 2M frame
    size_t rb = 0;
    err = frame_alloc(&caps[5], X86_64_LARGE_PAGE_SIZE, &rb);
    if (err_is_fail(err) || rb != X86_64_LARGE_PAGE_SIZE) {
        debug_printf("frame_alloc: %s (%ld)\n", err_getstring(err), err);
        return 1;
    }
    // cap 7: 1G frame
    err = frame_alloc(&caps[6], X86_64_HUGE_PAGE_SIZE, &rb);
    if (err_is_fail(err) || rb != X86_64_HUGE_PAGE_SIZE) {
        debug_printf("frame_alloc: %s (%ld)\n", err_getstring(err), err);
        return 1;
    }

    paging_x86_64_flags_t attr = 0;
    // select dest (ignore frame, asserts)
    for (int i = 0; i < 4; i++) {
        // select source
        for (int j = 0; j < 7; j++) {
            if (j >= 4) {
                // frame
                attr = FRAME_ACCESS_DEFAULT;
            } else {
                // ptable
                attr = PTABLE_ACCESS_DEFAULT;
            }
            // try mapping
            err = vnode_map(caps[i], caps[j], /*slot*/0, attr, /*off*/0,
                            /*count*/1, mapping);
            check_result(err, i, j);
            // unmap if mapping succeeded
            if (err_is_ok(err)) {
                err = vnode_unmap(caps[i], mapping);
                if (err_is_fail(err)) {
                    DEBUG_ERR(err, "vnode_unmap");
                }
                assert(err_is_ok(err));
                // XXX: better API?
                err = cap_delete(mapping);
                assert(err_is_ok(err));
            }
        }
    }

    printf("All tests executed: %d PASSED, %d FAILED\n", pass, fail);

    return 0;
}
Beispiel #13
0
static errval_t cow_init(size_t bufsize, size_t granularity,
        struct cnoderef *cow_cn, size_t *frame_count)
{
    assert(cow_cn);
    assert(frame_count);

    errval_t err;
    struct capref frame, cncap;
    struct cnoderef cnode;

    // get RAM cap bufsize = (bufsize / granularity + 1) * granularity;
    err = slot_alloc(&frame);
    assert(err_is_ok(err));
    size_t rambits = log2floor(bufsize);
    debug_printf("bits = %zu\n", rambits);
    err = ram_alloc(&frame, rambits);
    assert(err_is_ok(err));
    // calculate #slots
    cslot_t cap_count = bufsize / granularity;
    cslot_t slots;
    // get CNode
    err = cnode_create(&cncap, &cnode, cap_count, &slots);
    assert(err_is_ok(err));
    assert(slots >= cap_count);
    // retype RAM into Frames
    struct capref first_frame = (struct capref) { .cnode = cnode, .slot = 0 };
    err = cap_retype(first_frame, frame, ObjType_Frame, log2floor(granularity));
    assert(err_is_ok(err));
    err = cap_destroy(frame);
    assert(err_is_ok(err));
    *frame_count = slots;
    *cow_cn = cnode;
    return SYS_ERR_OK;
}

// create cow-enabled vregion & backing
// Can copy-on-write in granularity-sized chunks
static errval_t vspace_map_one_frame_cow(void **buf, size_t size,
        struct capref frame, vregion_flags_t flags,
        struct memobj **memobj, struct vregion **vregion,
        size_t granularity)
{
    errval_t err;
    if (!memobj) {
        memobj = malloc(sizeof(*memobj));
    }
    assert(memobj);
    if (!vregion) {
        vregion = malloc(sizeof(*vregion));
    }
    assert(vregion);
    err = vspace_map_anon_attr(buf, memobj, vregion, size, &size, flags);
    assert(err_is_ok(err));
    size_t chunks = size / granularity;
    cslot_t slots;
    struct capref cncap;
    struct cnoderef cnode;
    err = cnode_create(&cncap, &cnode, chunks, &slots);
    assert(err_is_ok(err));
    assert(slots >= chunks);
    struct capref fc = (struct capref) { .cnode = cnode, .slot = 0 };
    for (int i = 0; i < chunks; i++) {
        err = cap_copy(fc, frame);
        assert(err_is_ok(err));
        err = (*memobj)->f.fill_foff(*memobj, i * granularity, fc, granularity, i*granularity);
        assert(err_is_ok(err));
        err = (*memobj)->f.pagefault(*memobj, *vregion, i * granularity, 0);
        assert(err_is_ok(err));
        fc.slot++;
    }
    return SYS_ERR_OK;
}

int main(int argc, char *argv[])
{
    errval_t err;
    struct capref frame;
    size_t retsize;
    void *vbuf;
    struct vregion *vregion;
    uint8_t *buf;

    debug_printf("%s:%d\n", __FUNCTION__, __LINE__);
    err = frame_alloc(&frame, BUFSIZE, &retsize);
    assert(retsize >= BUFSIZE);
    if (err_is_fail(err)) {
        debug_printf("frame_alloc: %s\n", err_getstring(err));
        return 1;
    }
    debug_printf("%s:%d: %zu\n", __FUNCTION__, __LINE__, retsize);
    // setup region
    err = vspace_map_one_frame_attr(&vbuf, retsize, frame,
            VREGION_FLAGS_READ_WRITE, NULL, &vregion);
    if (err_is_fail(err)) {
        debug_printf("vspace_map: %s\n", err_getstring(err));
        return 1;
    }
    debug_printf("vaddr: %p\n", vbuf);

    // write stuff to region
    buf = vbuf;
    debug_printf("%s:%d: %p, %lu pages\n", __FUNCTION__, __LINE__, buf, BUFSIZE / BASE_PAGE_SIZE);
    memset(buf, 0xAA, BUFSIZE);

    debug_printf("%s:%d\n", __FUNCTION__, __LINE__);
    // create cow copy
    //  setup exception handler
    thread_set_exception_handler(handler, NULL, ex_stack,
            ex_stack+EX_STACK_SIZE, NULL, NULL);
    assert(err_is_ok(err));
    debug_printf("%s:%d\n", __FUNCTION__, __LINE__);
    err = cow_init(BUFSIZE, BASE_PAGE_SIZE, &cow_frames, &cow_frame_count);
    assert(err_is_ok(err));
    //  create r/o copy of region and tell exception handler bounds
    debug_printf("%s:%d\n", __FUNCTION__, __LINE__);
    err = vspace_map_one_frame_cow(&cow_vbuf, retsize, frame,
            VREGION_FLAGS_READ, NULL, &cow_vregion, BASE_PAGE_SIZE);
    if (err_is_fail(err)) {
        debug_printf("vspace_map: %s\n", err_getstring(err));
        return 1;
    }
    debug_printf("cow_vaddr: %p\n", cow_vbuf);

    // do stuff cow copy
    uint8_t *cbuf = cow_vbuf;
    for (int i = 0; i < BUFSIZE / BASE_PAGE_SIZE; i+=2) {
        cbuf[i * BASE_PAGE_SIZE + 1] = 0x55;
    }
    // verify results
    for (int i = 0; i < BUFSIZE / BASE_PAGE_SIZE; i++) {
        printf("page %d\n", i);
        printf("buf[0] = %d; cbuf[0] = %d\n", buf[i*BASE_PAGE_SIZE],
                cbuf[i*BASE_PAGE_SIZE]);
        printf("buf[1] = %d; cbuf[1] = %d\n", buf[i*BASE_PAGE_SIZE+1],
                cbuf[i*BASE_PAGE_SIZE+1]);
    }
    debug_dump_hw_ptables();
    return EXIT_SUCCESS;
}