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 (32UL*1024) 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 size_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 if (!memobj) { 115 memobj = malloc(sizeof(*memobj)); 116 } 117 assert(memobj); 118 if (!vregion) { 119 vregion = malloc(sizeof(*vregion)); 120 } 121 assert(vregion); 122 err = vspace_map_anon_attr(buf, memobj, vregion, size, &size, flags); 123 assert(err_is_ok(err)); 124 size_t chunks = size / granularity; 125 cslot_t slots; 126 struct capref cncap; 127 struct cnoderef cnode; 128 err = cnode_create(&cncap, &cnode, chunks, &slots); 129 assert(err_is_ok(err)); 130 assert(slots >= chunks); 131 struct capref fc = (struct capref) { .cnode = cnode, .slot = 0 }; 132 for (int i = 0; i < chunks; i++) { 133 err = cap_copy(fc, frame); 134 assert(err_is_ok(err)); 135 err = (*memobj)->f.fill_foff(*memobj, i * granularity, fc, granularity, i*granularity); 136 assert(err_is_ok(err)); 137 err = (*memobj)->f.pagefault(*memobj, *vregion, i * granularity, 0); 138 assert(err_is_ok(err)); 139 fc.slot++; 140 } 141 return SYS_ERR_OK; 142} 143 144int main(int argc, char *argv[]) 145{ 146 errval_t err; 147 struct capref frame; 148 size_t retsize; 149 void *vbuf; 150 struct vregion *vregion; 151 uint8_t *buf; 152 153 debug_printf("%s:%d\n", __FUNCTION__, __LINE__); 154 err = frame_alloc(&frame, BUFSIZE, &retsize); 155 assert(retsize >= BUFSIZE); 156 if (err_is_fail(err)) { 157 debug_printf("frame_alloc: %s\n", err_getstring(err)); 158 return 1; 159 } 160 debug_printf("%s:%d: %zu\n", __FUNCTION__, __LINE__, retsize); 161 // setup region 162 err = vspace_map_one_frame_attr(&vbuf, retsize, frame, 163 VREGION_FLAGS_READ_WRITE, NULL, &vregion); 164 if (err_is_fail(err)) { 165 debug_printf("vspace_map: %s\n", err_getstring(err)); 166 return 1; 167 } 168 debug_printf("vaddr: %p\n", vbuf); 169 170 // write stuff to region 171 buf = vbuf; 172 debug_printf("%s:%d: %p, %lu pages\n", __FUNCTION__, __LINE__, buf, BUFSIZE / BASE_PAGE_SIZE); 173 memset(buf, 0xAA, BUFSIZE); 174 175 debug_printf("%s:%d\n", __FUNCTION__, __LINE__); 176 // create cow copy 177 // setup exception handler 178 thread_set_exception_handler(handler, NULL, ex_stack, 179 ex_stack+EX_STACK_SIZE, NULL, NULL); 180 assert(err_is_ok(err)); 181 debug_printf("%s:%d\n", __FUNCTION__, __LINE__); 182 err = cow_init(BUFSIZE, BASE_PAGE_SIZE, &cow_frames, &cow_frame_count); 183 assert(err_is_ok(err)); 184 // create r/o copy of region and tell exception handler bounds 185 debug_printf("%s:%d\n", __FUNCTION__, __LINE__); 186 err = vspace_map_one_frame_cow(&cow_vbuf, retsize, frame, 187 VREGION_FLAGS_READ, NULL, &cow_vregion, BASE_PAGE_SIZE); 188 if (err_is_fail(err)) { 189 debug_printf("vspace_map: %s\n", err_getstring(err)); 190 return 1; 191 } 192 debug_printf("cow_vaddr: %p\n", cow_vbuf); 193 194 // do stuff cow copy 195 uint8_t *cbuf = cow_vbuf; 196 for (int i = 0; i < BUFSIZE / BASE_PAGE_SIZE; i+=2) { 197 cbuf[i * BASE_PAGE_SIZE + 1] = 0x55; 198 } 199 // verify results 200 for (int i = 0; i < BUFSIZE / BASE_PAGE_SIZE; i++) { 201 printf("page %d\n", i); 202 printf("buf[0] = %d; cbuf[0] = %d\n", buf[i*BASE_PAGE_SIZE], 203 cbuf[i*BASE_PAGE_SIZE]); 204 printf("buf[1] = %d; cbuf[1] = %d\n", buf[i*BASE_PAGE_SIZE+1], 205 cbuf[i*BASE_PAGE_SIZE+1]); 206 } 207 debug_dump_hw_ptables(); 208 return EXIT_SUCCESS; 209} 210