1/** \file 2 * \brief Copy-on-write example application 3 */ 4 5/* 6 * Copyright (c) 2015, ETH Zurich. 7 * All rights reserved. 8 * 9 * This file is distributed under the terms in the attached LICENSE file. 10 * If you do not find this file, copies can be found by writing to: 11 * ETH Zurich D-INFK, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group. 12 */ 13 14#include <barrelfish/barrelfish.h> 15#include <barrelfish/except.h> 16#include <barrelfish/threads.h> 17#include <barrelfish/sys_debug.h> 18#include <stdio.h> 19#include <stdlib.h> 20#define BUFSIZE (67108864UL) 21 22#define EX_STACK_SIZE 16384 23static char ex_stack[EX_STACK_SIZE]; 24static struct vregion *cow_vregion; 25static void *cow_vbuf; 26static struct cnoderef cow_frames; 27static size_t cow_frame_count = 0; 28 29static void handler(enum exception_type type, int subtype, void *vaddr, 30 arch_registers_state_t *regs) 31{ 32 debug_printf("got exception %d(%d) on %p\n", type, subtype, vaddr); 33 assert(type == EXCEPT_PAGEFAULT); 34 assert(subtype == PAGEFLT_WRITE); 35 uintptr_t addr = (uintptr_t) vaddr; 36 uintptr_t faddr = addr & ~BASE_PAGE_MASK; 37 uintptr_t base = (uintptr_t) cow_vbuf; 38 if (addr < base || addr >= base + BUFSIZE) { 39 debug_printf("unexpected write pagefault on %p\n", vaddr); 40 exit(1); 41 } 42 assert(cow_frame_count); 43 debug_printf("got expected write pagefault on %p, creating copy of page\n", vaddr); 44 // get and map copy of page 45 size_t frame_id = (addr - base) / BASE_PAGE_SIZE; 46 debug_printf("remapping frame %zu\n", frame_id); 47 struct memobj *m = vregion_get_memobj(cow_vregion); 48 assert(m); 49 errval_t err; 50 struct capref retframe; 51 struct capref f = (struct capref) { .cnode = cow_frames, .slot = frame_id }; 52 genvaddr_t retoff; 53 struct vregion *vr; 54 void *buf; 55 // copy data from faulting page to new page 56 err = vspace_map_one_frame(&buf, BASE_PAGE_SIZE, f, NULL, &vr); 57 assert(err_is_ok(err)); 58 memcpy(buf, (void *)faddr, BASE_PAGE_SIZE); 59 vregion_destroy(vr); 60 err = m->f.unfill(m, frame_id * BASE_PAGE_SIZE, &retframe, &retoff); 61 assert(err_is_ok(err)); 62 err = m->f.fill(m, frame_id * BASE_PAGE_SIZE, f, BASE_PAGE_SIZE); 63 assert(err_is_ok(err)); 64 err = m->f.pagefault(m, cow_vregion, frame_id * BASE_PAGE_SIZE, 0); 65 assert(err_is_ok(err)); 66 err = m->f.protect(m, cow_vregion, frame_id * BASE_PAGE_SIZE, 67 BASE_PAGE_SIZE, VREGION_FLAGS_READ_WRITE); 68 assert(err_is_ok(err)); 69} 70 71static errval_t cow_init(size_t bufsize, size_t granularity, 72 struct cnoderef *cow_cn, size_t *frame_count) 73{ 74 assert(cow_cn); 75 assert(frame_count); 76 77 errval_t err; 78 struct capref frame, cncap; 79 struct cnoderef cnode; 80 81 // get RAM cap bufsize = (bufsize / granularity + 1) * granularity; 82 err = slot_alloc(&frame); 83 assert(err_is_ok(err)); 84 size_t rambits = log2floor(bufsize); 85 debug_printf("bits = %zu\n", rambits); 86 err = ram_alloc(&frame, rambits); 87 assert(err_is_ok(err)); 88 // calculate #slots 89 cslot_t cap_count = bufsize / granularity; 90 cslot_t slots; 91 // get CNode 92 err = cnode_create(&cncap, &cnode, cap_count, &slots); 93 assert(err_is_ok(err)); 94 assert(slots >= cap_count); 95 // retype RAM into Frames 96 struct capref first_frame = (struct capref) { .cnode = cnode, .slot = 0 }; 97 err = cap_retype(first_frame, frame, 0, ObjType_Frame, granularity, cap_count); 98 assert(err_is_ok(err)); 99 err = cap_destroy(frame); 100 assert(err_is_ok(err)); 101 *frame_count = slots; 102 *cow_cn = cnode; 103 return SYS_ERR_OK; 104} 105 106// create cow-enabled vregion & backing 107// Can copy-on-write in granularity-sized chunks 108static errval_t vspace_map_one_frame_cow(void **buf, size_t size, 109 struct capref frame, vregion_flags_t flags, 110 struct memobj **memobj, struct vregion **vregion, 111 size_t granularity) 112{ 113 errval_t err; 114 // XXX: I belive these are going to be leaked: vspace_map_anon_attr() seems 115 // to call malloc() as well --KK 116 if (!memobj) { 117 memobj = malloc(sizeof(*memobj)); 118 } 119 assert(memobj); 120 if (!vregion) { 121 vregion = malloc(sizeof(*vregion)); 122 } 123 assert(vregion); 124 err = vspace_map_anon_attr(buf, memobj, vregion, size, &size, flags); 125 assert(err_is_ok(err)); 126 size_t chunks = size / granularity; 127 cslot_t slots; 128 struct capref cncap; 129 struct cnoderef cnode; 130 err = cnode_create(&cncap, &cnode, chunks, &slots); 131 assert(err_is_ok(err)); 132 assert(slots >= chunks); 133 struct capref fc = (struct capref) { .cnode = cnode, .slot = 0 }; 134 for (int i = 0; i < chunks; i++) { 135 err = cap_copy(fc, frame); 136 assert(err_is_ok(err)); 137 err = (*memobj)->f.fill_foff(*memobj, i * granularity, fc, granularity, i*granularity); 138 assert(err_is_ok(err)); 139 err = (*memobj)->f.pagefault(*memobj, *vregion, i * granularity, 0); 140 assert(err_is_ok(err)); 141 fc.slot++; 142 } 143 return SYS_ERR_OK; 144} 145 146int main(int argc, char *argv[]) 147{ 148 errval_t err; 149 struct capref frame; 150 size_t retsize; 151 void *vbuf; 152 struct vregion *vregion; 153 uint8_t *buf; 154 155 debug_printf("%s:%d\n", __FUNCTION__, __LINE__); 156 err = frame_alloc(&frame, BUFSIZE, &retsize); 157 assert(retsize >= BUFSIZE); 158 if (err_is_fail(err)) { 159 debug_printf("frame_alloc: %s\n", err_getstring(err)); 160 return 1; 161 } 162 debug_printf("%s:%d: %zu\n", __FUNCTION__, __LINE__, retsize); 163 // setup region 164 err = vspace_map_one_frame_attr(&vbuf, retsize, frame, 165 VREGION_FLAGS_READ_WRITE, NULL, &vregion); 166 if (err_is_fail(err)) { 167 debug_printf("vspace_map: %s\n", err_getstring(err)); 168 return 1; 169 } 170 debug_printf("vaddr: %p\n", vbuf); 171 172 // write stuff to region 173 buf = vbuf; 174 debug_printf("%s:%d: %p, %lu pages\n", __FUNCTION__, __LINE__, buf, BUFSIZE / BASE_PAGE_SIZE); 175 memset(buf, 0xAA, BUFSIZE); 176 177 debug_printf("%s:%d\n", __FUNCTION__, __LINE__); 178 // create cow copy 179 // setup exception handler 180 thread_set_exception_handler(handler, NULL, ex_stack, 181 ex_stack+EX_STACK_SIZE, NULL, NULL); 182 assert(err_is_ok(err)); 183 debug_printf("%s:%d\n", __FUNCTION__, __LINE__); 184 err = cow_init(BUFSIZE, BASE_PAGE_SIZE, &cow_frames, &cow_frame_count); 185 assert(err_is_ok(err)); 186 // create r/o copy of region and tell exception handler bounds 187 debug_printf("%s:%d\n", __FUNCTION__, __LINE__); 188 err = vspace_map_one_frame_cow(&cow_vbuf, retsize, frame, 189 VREGION_FLAGS_READ, NULL, &cow_vregion, BASE_PAGE_SIZE); 190 if (err_is_fail(err)) { 191 debug_printf("vspace_map: %s\n", err_getstring(err)); 192 return 1; 193 } 194 debug_printf("cow_vaddr: %p\n", cow_vbuf); 195 196 // do stuff cow copy 197 uint8_t *cbuf = cow_vbuf; 198 for (int i = 0; i < BUFSIZE / BASE_PAGE_SIZE; i+=2) { 199 cbuf[i * BASE_PAGE_SIZE + 1] = 0x55; 200 } 201 // verify results 202 for (int i = 0; i < BUFSIZE / BASE_PAGE_SIZE; i++) { 203 printf("page %d\n", i); 204 printf("buf[0] = %d; cbuf[0] = %d\n", buf[i*BASE_PAGE_SIZE], 205 cbuf[i*BASE_PAGE_SIZE]); 206 printf("buf[1] = %d; cbuf[1] = %d\n", buf[i*BASE_PAGE_SIZE+1], 207 cbuf[i*BASE_PAGE_SIZE+1]); 208 } 209 debug_dump_hw_ptables(); 210 return EXIT_SUCCESS; 211} 212