1/**
2 * \file
3 * \brief Creates mapping based on the MMU type
4 *
5 * If the MMU supports translations, then anonymous type is used
6 * (more efficient) else non contiguous memory is used.
7 */
8
9/*
10 * Copyright (c) 2010, 2011, ETH Zurich.
11 * Copyright (c) 2014, HP Labs.
12 * All rights reserved.
13 *
14 * This file is distributed under the terms in the attached LICENSE file.
15 * If you do not find this file, copies can be found by writing to:
16 * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
17 */
18
19#include <barrelfish/barrelfish.h>
20#include <barrelfish/vspace_mmu_aware.h>
21#include <barrelfish/core_state.h>
22#include <string.h>
23
24/// Minimum free memory before we return it to memory server
25#define MIN_MEM_FOR_FREE        (1 * 1024 * 1024)
26
27void vspace_mmu_aware_set_slot_alloc(struct vspace_mmu_aware *state,
28                                     struct slot_allocator *slot_allocator)
29{
30    if (slot_allocator) {
31        state->slot_alloc = slot_allocator;
32    } else {
33        state->slot_alloc = get_default_slot_allocator();
34    }
35}
36
37/**
38 * \brief Initialize vspace_mmu_aware struct
39 *
40 * \param state   The struct to initialize
41 * \param init    The buffer to use to initialize the struct
42 * \param size    The size of anon memobj to create
43 *
44 * Initializes the struct according to the type of MMU
45 */
46errval_t vspace_mmu_aware_init(struct vspace_mmu_aware *state, size_t size)
47{
48    return vspace_mmu_aware_init_aligned(state, NULL, size, 0,
49            VREGION_FLAGS_READ_WRITE);
50}
51
52errval_t vspace_mmu_aware_init_aligned(struct vspace_mmu_aware *state,
53                                       struct slot_allocator *slot_allocator,
54                                       size_t size, size_t alignment,
55                                       vregion_flags_t flags)
56{
57    state->size = size;
58    state->consumed = 0;
59    state->alignment = alignment;
60
61    vspace_mmu_aware_set_slot_alloc(state, slot_allocator);
62
63    errval_t err;
64
65    size = ROUND_UP(size, BASE_PAGE_SIZE);
66    err = memobj_create_anon(&state->memobj, size, 0);
67    if (err_is_fail(err)) {
68        return err_push(err, LIB_ERR_MEMOBJ_CREATE_ANON);
69    }
70
71    err = vregion_map_aligned(&state->vregion, get_current_vspace(),
72                              &state->memobj.m, 0, size,
73                              flags, alignment);
74    if (err_is_fail(err)) {
75        return err_push(err, LIB_ERR_VREGION_MAP);
76    }
77    state->offset = state->mapoffset = 0;
78
79    return SYS_ERR_OK;
80}
81
82/**
83 * \brief Create mappings
84 *
85 * \param state     The object metadata
86 * \param frame     An empty slot to place the frame capability in
87 * \param req_size  The required amount by the application
88 * \param retbuf    Pointer to return the mapped buffer
89 * \param retsize   The actual size returned
90 *
91 * This function will returns a special error code if frame_create
92 * fails due to the constrains to the memory server (amount of memory
93 * or region of memory). This is to facilitate retrying with different
94 * constraints.
95 */
96errval_t vspace_mmu_aware_map(struct vspace_mmu_aware *state, size_t req_size,
97                              void **retbuf, size_t *retsize)
98{
99    errval_t err;
100
101    struct capref frame;
102
103    // Calculate how much still to map in
104    size_t origsize = req_size;
105    assert(state->mapoffset >= state->offset);
106    if(state->mapoffset - state->offset > req_size) {
107        req_size = 0;
108    } else {
109        req_size -= state->mapoffset - state->offset;
110    }
111    size_t alloc_size = ROUND_UP(req_size, BASE_PAGE_SIZE);
112    size_t ret_size = 0;
113
114    if (req_size > 0) {
115#if __x86_64__
116        if ((state->vregion.flags & VREGION_FLAGS_HUGE) &&
117            (state->mapoffset & HUGE_PAGE_MASK) == 0)
118        {
119            // this is an opportunity to switch to 1G pages if requested.
120            // we know that we can use large pages without jumping through hoops
121            // if state->vregion.flags has VREGION_FLAGS_HUGE set and
122            // mapoffset is aligned to at least HUGE_PAGE_SIZE.
123            alloc_size = ROUND_UP(req_size, HUGE_PAGE_SIZE);
124
125            // goto allocation directly so we can avoid nasty code interaction
126            // between #if __x86_64__ and the size checks, we want to be able
127            // to use 2M pages on x86_64 also. -SG, 2015-04-30.
128            goto allocate;
129        }
130#endif
131        if ((state->vregion.flags & VREGION_FLAGS_LARGE) &&
132            (state->mapoffset & LARGE_PAGE_MASK) == 0)
133        {
134            // this is an opportunity to switch to 2M pages if requested.
135            // we know that we can use large pages without jumping through hoops
136            // if state->vregion.flags has VREGION_FLAGS_LARGE set and
137            // mapoffset is aligned to at least LARGE_PAGE_SIZE.
138            alloc_size = ROUND_UP(req_size, LARGE_PAGE_SIZE);
139        }
140        // Create frame of appropriate size
141allocate:
142        err = state->slot_alloc->alloc(state->slot_alloc, &frame);
143        if (err_is_fail(err)) {
144            return err_push(err, LIB_ERR_SLOT_ALLOC_NO_SPACE);
145        }
146
147        err = frame_create(frame, alloc_size, &ret_size);
148        if (err_is_fail(err)) {
149            if (err_no(err) == LIB_ERR_RAM_ALLOC_MS_CONSTRAINTS) {
150                // we can only get 4k frames for now; retry with 4k
151                if (alloc_size > BASE_PAGE_SIZE && req_size <= BASE_PAGE_SIZE) {
152                    alloc_size = BASE_PAGE_SIZE;
153                    goto allocate;
154                }
155                return err_push(err, LIB_ERR_FRAME_CREATE_MS_CONSTRAINTS);
156            }
157            return err_push(err, LIB_ERR_FRAME_CREATE);
158        }
159        assert(ret_size >= req_size);
160        origsize += ret_size - req_size;
161        req_size = ret_size;
162
163        if (state->consumed + req_size > state->size) {
164            err = cap_delete(frame);
165            if (err_is_fail(err)) {
166                debug_err(__FILE__, __func__, __LINE__, err,
167                          "cap_delete failed");
168            }
169            state->slot_alloc->free(state->slot_alloc, frame);
170            return LIB_ERR_VSPACE_MMU_AWARE_NO_SPACE;
171        }
172
173        // Map it in
174        err = state->memobj.m.f.fill(&state->memobj.m, state->mapoffset, frame,
175                                     req_size);
176        if (err_is_fail(err)) {
177            return err_push(err, LIB_ERR_MEMOBJ_FILL);
178        }
179        err = state->memobj.m.f.pagefault(&state->memobj.m, &state->vregion,
180                                          state->mapoffset, 0);
181        if (err_is_fail(err)) {
182            return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
183        }
184    }
185
186    // Return buffer
187    genvaddr_t gvaddr = vregion_get_base_addr(&state->vregion) + state->offset;
188    *retbuf = (void*)vspace_genvaddr_to_lvaddr(gvaddr);
189    *retsize = origsize;
190    state->mapoffset += req_size;
191    state->offset += origsize;
192    state->consumed += origsize;
193
194    return SYS_ERR_OK;
195}
196
197errval_t vspace_mmu_aware_reset(struct vspace_mmu_aware *state,
198                                struct capref frame, size_t size)
199{
200    errval_t err;
201    struct vregion *vregion;
202    struct capref oldframe;
203    void *vbuf;
204
205    // create copy of new region
206    err = slot_alloc(&oldframe);
207    if (err_is_fail(err)) {
208        return err;
209    }
210    err = cap_copy(oldframe, frame);
211    if (err_is_fail(err)) {
212        return err;
213    }
214    err = vspace_map_one_frame_attr_aligned(&vbuf, size, oldframe,
215            VREGION_FLAGS_READ_WRITE | VREGION_FLAGS_LARGE, LARGE_PAGE_SIZE,
216            NULL, &vregion);
217    if (err_is_fail(err)) {
218        return err;
219    }
220    // copy over data to new frame
221    genvaddr_t gen_base = vregion_get_base_addr(&state->vregion);
222    memcpy(vbuf, (void*)(lvaddr_t)gen_base, state->mapoffset);
223
224    err = vregion_destroy(vregion);
225    if (err_is_fail(err)) {
226        return err;
227    }
228
229    genvaddr_t offset = 0;
230    // Unmap backing frames for [0, size) in state.vregion
231    do {
232        err = state->memobj.m.f.unfill(&state->memobj.m, 0, &oldframe,
233                &offset);
234        if (err_is_fail(err) &&
235            err_no(err) != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET)
236        {
237            return err_push(err, LIB_ERR_MEMOBJ_UNMAP_REGION);
238        }
239        struct frame_identity fi;
240        // increase address
241        err = frame_identify(oldframe, &fi);
242        if (err_is_fail(err)) {
243            return err;
244        }
245        offset += fi.bytes;
246        err = cap_destroy(oldframe);
247        if (err_is_fail(err)) {
248            return err;
249        }
250    } while(offset < state->mapoffset);
251
252    // Map new frame in
253    err = state->memobj.m.f.fill(&state->memobj.m, 0, frame, size);
254    if (err_is_fail(err)) {
255        return err_push(err, LIB_ERR_MEMOBJ_FILL);
256    }
257    err = state->memobj.m.f.pagefault(&state->memobj.m, &state->vregion, 0, 0);
258    if (err_is_fail(err)) {
259        return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
260    }
261
262    state->mapoffset = size;
263    return SYS_ERR_OK;
264}
265
266errval_t vspace_mmu_aware_unmap(struct vspace_mmu_aware *state,
267                                lvaddr_t base, size_t bytes)
268{
269    errval_t err;
270    struct capref frame;
271    genvaddr_t gvaddr = vregion_get_base_addr(&state->vregion) + state->offset;
272    lvaddr_t eaddr = vspace_genvaddr_to_lvaddr(gvaddr);
273    genvaddr_t offset;
274    genvaddr_t gen_base = vspace_lvaddr_to_genvaddr(base)
275        - vregion_get_base_addr(&state->vregion);
276    genvaddr_t min_offset = 0;
277    bool success = false;
278
279    assert(vspace_lvaddr_to_genvaddr(base) >= vregion_get_base_addr(&state->vregion));
280    assert(base + bytes == (lvaddr_t)eaddr);
281
282    assert(bytes <= state->consumed);
283    assert(bytes <= state->offset);
284
285    // Reduce offset
286    state->offset -= bytes;
287    state->consumed -= bytes;
288
289    // Free only in bigger blocks
290    if(state->mapoffset - state->offset > MIN_MEM_FOR_FREE) {
291        do {
292            // Unmap and return (via unfill) frames from base
293            err = state->memobj.m.f.unfill(&state->memobj.m, gen_base,
294                                           &frame, &offset);
295            if(err_is_fail(err) && err_no(err) != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET) {
296                return err_push(err, LIB_ERR_MEMOBJ_UNMAP_REGION);
297            }
298
299            // Delete frame cap
300            if(err_is_ok(err)) {
301                success = true;
302                if (min_offset == 0 || min_offset > offset) {
303                    min_offset = offset;
304                }
305
306                err = cap_destroy(frame);
307                if(err_is_fail(err)) {
308                    return err;
309                }
310            }
311        } while(err != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET);
312
313//    state->consumed -= bytes;
314        if (success) {
315            state->mapoffset = min_offset;
316        }
317    }
318
319    return SYS_ERR_OK;
320}
321