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, Universitaetstrasse 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
52static size_t pagesize_from_vregion_flags(vregion_flags_t flags)
53{
54#ifdef __x86_64__
55    if (flags & VREGION_FLAGS_HUGE) {
56        return HUGE_PAGE_SIZE;
57    }
58#endif
59    if (flags & VREGION_FLAGS_LARGE) {
60        return LARGE_PAGE_SIZE;
61    }
62    return BASE_PAGE_SIZE;
63}
64
65errval_t vspace_mmu_aware_init_aligned(struct vspace_mmu_aware *state,
66                                       struct slot_allocator *slot_allocator,
67                                       size_t size, size_t alignment,
68                                       vregion_flags_t flags)
69{
70    state->size = size;
71    state->consumed = 0;
72    state->alignment = alignment;
73    state->pagesize = pagesize_from_vregion_flags(flags);
74
75    vspace_mmu_aware_set_slot_alloc(state, slot_allocator);
76
77    errval_t err;
78
79    size = ROUND_UP(size, BASE_PAGE_SIZE);
80    err = memobj_create_anon(&state->memobj, size, 0);
81    if (err_is_fail(err)) {
82        return err_push(err, LIB_ERR_MEMOBJ_CREATE_ANON);
83    }
84
85    err = vregion_map_aligned(&state->vregion, get_current_vspace(),
86                              &state->memobj.m, 0, size,
87                              flags, alignment);
88    if (err_is_fail(err)) {
89        return err_push(err, LIB_ERR_VREGION_MAP);
90    }
91    state->offset = state->mapoffset = 0;
92
93    return SYS_ERR_OK;
94}
95
96/**
97 * \brief Create mappings
98 *
99 * \param state     The object metadata
100 * \param frame     An empty slot to place the frame capability in
101 * \param req_size  The required amount by the application
102 * \param retbuf    Pointer to return the mapped buffer
103 * \param retsize   The actual size returned
104 *
105 * This function will returns a special error code if frame_create
106 * fails due to the constrains to the memory server (amount of memory
107 * or region of memory). This is to facilitate retrying with different
108 * constraints.
109 */
110errval_t vspace_mmu_aware_map(struct vspace_mmu_aware *state, size_t req_size,
111                              void **retbuf, size_t *retsize)
112{
113    errval_t err;
114
115    struct capref frame;
116
117    // Calculate how much still to map in
118    size_t origsize = req_size;
119    assert(state->mapoffset >= state->offset);
120    if(state->mapoffset - state->offset > req_size) {
121        req_size = 0;
122    } else {
123        req_size -= state->mapoffset - state->offset;
124    }
125    size_t alloc_size = ROUND_UP(req_size, BASE_PAGE_SIZE);
126    size_t ret_size = 0;
127
128    if (req_size > 0) {
129#if __x86_64__
130        if ((state->vregion.flags & VREGION_FLAGS_HUGE) &&
131            (state->mapoffset & HUGE_PAGE_MASK) == 0)
132        {
133            // this is an opportunity to switch to 1G pages if requested.
134            // we know that we can use large pages without jumping through hoops
135            // if state->vregion.flags has VREGION_FLAGS_HUGE set and
136            // mapoffset is aligned to at least HUGE_PAGE_SIZE.
137            alloc_size = ROUND_UP(req_size, HUGE_PAGE_SIZE);
138
139            // goto allocation directly so we can avoid nasty code interaction
140            // between #if __x86_64__ and the size checks, we want to be able
141            // to use 2M pages on x86_64 also. -SG, 2015-04-30.
142            goto allocate;
143        }
144#endif
145        if ((state->vregion.flags & VREGION_FLAGS_LARGE) &&
146            (state->mapoffset & LARGE_PAGE_MASK) == 0)
147        {
148            // this is an opportunity to switch to 2M pages if requested.
149            // we know that we can use large pages without jumping through hoops
150            // if state->vregion.flags has VREGION_FLAGS_LARGE set and
151            // mapoffset is aligned to at least LARGE_PAGE_SIZE.
152            alloc_size = ROUND_UP(req_size, LARGE_PAGE_SIZE);
153        }
154        // allocate slot before jump target
155        err = state->slot_alloc->alloc(state->slot_alloc, &frame);
156        if (err_is_fail(err)) {
157            return err_push(err, LIB_ERR_SLOT_ALLOC_NO_SPACE);
158        }
159allocate:
160        // Create frame of appropriate size
161        err = frame_create(frame, alloc_size, &ret_size);
162        if (err_is_fail(err)) {
163            if (err_no(err) == LIB_ERR_RAM_ALLOC_MS_CONSTRAINTS) {
164                // we can only get 4k frames for now; retry with 4k
165                if (alloc_size > BASE_PAGE_SIZE && req_size <= BASE_PAGE_SIZE) {
166                    alloc_size = BASE_PAGE_SIZE;
167                    goto allocate;
168                }
169                return err_push(err, LIB_ERR_FRAME_CREATE_MS_CONSTRAINTS);
170            }
171            return err_push(err, LIB_ERR_FRAME_CREATE);
172        }
173        assert(ret_size >= req_size);
174        origsize += ret_size - req_size;
175        req_size = ret_size;
176
177        if (state->consumed + req_size > state->size) {
178            err = cap_delete(frame);
179            if (err_is_fail(err)) {
180                debug_err(__FILE__, __func__, __LINE__, err,
181                          "cap_delete failed");
182            }
183            state->slot_alloc->free(state->slot_alloc, frame);
184            return LIB_ERR_VSPACE_MMU_AWARE_NO_SPACE;
185        }
186
187        // Map it in
188        err = state->memobj.m.f.fill(&state->memobj.m, state->mapoffset, frame,
189                                     req_size);
190        if (err_is_fail(err)) {
191            return err_push(err, LIB_ERR_MEMOBJ_FILL);
192        }
193        err = state->memobj.m.f.pagefault(&state->memobj.m, &state->vregion,
194                                          state->mapoffset, 0);
195        if (err_is_fail(err)) {
196            return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
197        }
198    }
199
200    // Return buffer
201    genvaddr_t gvaddr = vregion_get_base_addr(&state->vregion) + state->offset;
202    *retbuf = (void*)vspace_genvaddr_to_lvaddr(gvaddr);
203    *retsize = origsize;
204    state->mapoffset += req_size;
205    state->offset += origsize;
206    state->consumed += origsize;
207
208    return SYS_ERR_OK;
209}
210
211errval_t vspace_mmu_aware_reset(struct vspace_mmu_aware *state,
212                                struct capref frame, size_t size)
213{
214    errval_t err;
215    struct vregion *vregion;
216    struct capref oldframe;
217    void *vbuf;
218
219    // create copy of new region
220    err = slot_alloc(&oldframe);
221    if (err_is_fail(err)) {
222        return err;
223    }
224    err = cap_copy(oldframe, frame);
225    if (err_is_fail(err)) {
226        return err;
227    }
228    err = vspace_map_one_frame_attr_aligned(&vbuf, size, oldframe,
229            VREGION_FLAGS_READ_WRITE | VREGION_FLAGS_LARGE, LARGE_PAGE_SIZE,
230            NULL, &vregion);
231    if (err_is_fail(err)) {
232        return err;
233    }
234    // copy over data to new frame
235    genvaddr_t gen_base = vregion_get_base_addr(&state->vregion);
236    memcpy(vbuf, (void*)(lvaddr_t)gen_base, state->mapoffset);
237
238    err = vregion_destroy(vregion);
239    if (err_is_fail(err)) {
240        return err;
241    }
242
243    genvaddr_t offset = 0;
244    // Unmap backing frames for [0, size) in state.vregion
245    do {
246        err = state->memobj.m.f.unfill(&state->memobj.m, 0, &oldframe,
247                &offset);
248        if (err_is_fail(err) &&
249            err_no(err) != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET)
250        {
251            return err_push(err, LIB_ERR_MEMOBJ_UNMAP_REGION);
252        }
253        struct frame_identity fi;
254        // increase address
255        err = frame_identify(oldframe, &fi);
256        if (err_is_fail(err)) {
257            return err;
258        }
259        offset += fi.bytes;
260        err = cap_destroy(oldframe);
261        if (err_is_fail(err)) {
262            return err;
263        }
264    } while(offset < state->mapoffset);
265
266    // Map new frame in
267    err = state->memobj.m.f.fill(&state->memobj.m, 0, frame, size);
268    if (err_is_fail(err)) {
269        return err_push(err, LIB_ERR_MEMOBJ_FILL);
270    }
271    err = state->memobj.m.f.pagefault(&state->memobj.m, &state->vregion, 0, 0);
272    if (err_is_fail(err)) {
273        return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
274    }
275
276    state->mapoffset = size;
277    return SYS_ERR_OK;
278}
279
280errval_t vspace_mmu_aware_unmap(struct vspace_mmu_aware *state,
281                                lvaddr_t base, size_t bytes)
282{
283    errval_t err;
284    struct capref frame;
285    genvaddr_t gvaddr = vregion_get_base_addr(&state->vregion) + state->offset;
286    lvaddr_t eaddr = vspace_genvaddr_to_lvaddr(gvaddr);
287    genvaddr_t offset;
288    genvaddr_t gen_base = vspace_lvaddr_to_genvaddr(base)
289        - vregion_get_base_addr(&state->vregion);
290    genvaddr_t min_offset = 0;
291    bool success = false;
292
293    assert(vspace_lvaddr_to_genvaddr(base) >= vregion_get_base_addr(&state->vregion));
294    assert(base + bytes == (lvaddr_t)eaddr);
295
296    assert(bytes <= state->consumed);
297    assert(bytes <= state->offset);
298
299    // Reduce offset
300    state->offset -= bytes;
301    state->consumed -= bytes;
302
303    // Free only in bigger blocks
304    if(state->mapoffset - state->offset > MIN_MEM_FOR_FREE) {
305        do {
306            // Unmap and return (via unfill) frames from base
307            err = state->memobj.m.f.unfill(&state->memobj.m, gen_base,
308                                           &frame, &offset);
309            if(err_is_fail(err) && err_no(err) != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET) {
310                return err_push(err, LIB_ERR_MEMOBJ_UNMAP_REGION);
311            }
312
313            // Delete frame cap
314            if(err_is_ok(err)) {
315                success = true;
316                if (min_offset == 0 || min_offset > offset) {
317                    min_offset = offset;
318                }
319
320                err = cap_destroy(frame);
321                if(err_is_fail(err)) {
322                    return err;
323                }
324            }
325        } while(err != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET);
326
327//    state->consumed -= bytes;
328        if (success) {
329            state->mapoffset = min_offset;
330        }
331    }
332
333    return SYS_ERR_OK;
334}
335