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