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