1/**
2 * \file
3 * \brief vspace management
4 *
5 * A vspace consists of a set of vregions and one pmap.
6 * The current vspace is setup by the (domain/dispatcher?) spawning it.
7 *
8 * Warning: slot_alloc_init calls vregion_map which calls vspace_add_vregion.
9 * vspace_add_vregion uses malloc to increase it's slab.
10 * Since malloc depends upon slot_alloc_init being called successfully,
11 * vspace_add_vregion should have enough initial slab space to not use malloc.
12 */
13
14/*
15 * Copyright (c) 2009, 2010, 2011, ETH Zurich.
16 * All rights reserved.
17 *
18 * This file is distributed under the terms in the attached LICENSE file.
19 * If you do not find this file, copies can be found by writing to:
20 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
21 */
22
23#include <barrelfish/barrelfish.h>
24#include <barrelfish/core_state.h>
25#include "vspace_internal.h"
26#include <stdio.h>
27
28/**
29 * \brief Initialize the current vspace structure
30 *
31 * This code is coupled with #pmap_current_init()
32 */
33errval_t vspace_current_init(bool init_domain)
34{
35    errval_t err;
36    struct vspace *vspace = get_current_vspace();
37    struct pmap *pmap = get_current_pmap();
38
39    vspace->pmap = pmap;
40    vspace->head = NULL;
41
42    // Setup the layout
43    err = vspace_layout_init(&vspace->layout);
44    if (err_is_fail(err)) {
45        return err_push(err, LIB_ERR_VSPACE_LAYOUT_INIT);
46    }
47
48    // Initialize current pmap
49    struct capref cap = {
50        .cnode = cnode_page,
51        .slot  = 0,
52    };
53    err = pmap_init(pmap, vspace, cap, NULL);
54    if (err_is_fail(err)) {
55        return err_push(err, LIB_ERR_PMAP_INIT);
56    }
57    err = pmap_current_init(init_domain);
58    if (err_is_fail(err)) {
59        return err_push(err, LIB_ERR_PMAP_CURRENT_INIT);
60    }
61
62    // Initialize pinned memory
63    err = vspace_pinned_init();
64    if (err_is_fail(err)) {
65        return err_push(err, LIB_ERR_VSPACE_PINNED_INIT);
66    }
67
68    return SYS_ERR_OK;
69}
70
71/**
72 * \brief Add a new region into the vspace
73 *
74 * \param point  The vspace struct
75 * \param region The region to add
76 *
77 * pmap implementation rely on vspace maintaining an ordered list of vregions
78 */
79errval_t vspace_add_vregion(struct vspace *vspace, struct vregion *region)
80{
81    // Sanity-check region (TODO: return error?)
82    assert(region->size > 0);
83    assert(region->base + region->size > region->base);
84
85    if (vspace->head == NULL) {
86        vspace->head = region;
87        region->next = NULL;
88        return SYS_ERR_OK;
89    }
90
91    // Insert in order
92    struct vregion *walk = vspace->head;
93    struct vregion *prev = NULL;
94    while (walk != NULL) {
95        if (region->base <= walk->base) {
96            /* check for overlaps! */
97            if (region->base + region->size > walk->base
98                || (prev != NULL && prev->base + prev->size > region->base)) {
99                return LIB_ERR_VSPACE_REGION_OVERLAP;
100            }
101
102            /* add here */
103            if (prev == NULL) {
104                region->next = vspace->head;
105                vspace->head = region;
106            } else {
107                prev->next = region;
108                region->next = walk;
109            }
110            return SYS_ERR_OK;
111        }
112
113        prev = walk;
114        walk = walk->next;
115    }
116
117    /* add to end of list, checking for overlap with last item */
118    assert(prev != NULL);
119    if (prev->base + prev->size > region->base) {
120        return LIB_ERR_VSPACE_REGION_OVERLAP;
121    }
122    prev->next = region;
123    region->next = NULL;
124    return SYS_ERR_OK;
125}
126
127/**
128 * \brief remove a region from the vspace
129 *
130 * \param point  The vspace struct
131 * \param region The region to remove
132 *
133 * Library internal function
134 */
135errval_t vspace_remove_vregion(struct vspace *vspace, struct vregion* region)
136{
137    assert(vspace != NULL);
138    struct vregion *walk = vspace->head;
139    struct vregion *prev = NULL;
140
141    while (walk) {
142        if (walk == region) {
143            if (prev) {
144                assert(prev->next == walk);
145                prev->next = walk->next;
146            } else {
147                assert(walk == vspace->head);
148                vspace->head = walk->next;
149            }
150            return SYS_ERR_OK;
151        }
152        prev = walk;
153        walk = walk->next;
154    }
155
156    return LIB_ERR_VREGION_NOT_FOUND;
157}
158
159/**
160 * \brief Initialize a vspace
161 *
162 * \param vspace The vspace to initialize
163 * \param pmap   The pmap to associate with the vspace
164 *
165 * Initializes a vspace, associating it with a pmap
166 */
167errval_t vspace_init(struct vspace *vspace, struct pmap *pmap)
168{
169    errval_t err;
170
171    vspace->pmap = pmap;
172    vspace->head = NULL;
173
174    // Setup the layout
175    err = vspace_layout_init(&vspace->layout);
176    if (err_is_fail(err)) {
177        return err_push(err, LIB_ERR_VSPACE_LAYOUT_INIT);
178    }
179
180    return SYS_ERR_OK;
181}
182
183/**
184 * \brief Destroy a vspace
185 */
186errval_t vspace_destroy(struct vspace *vspace)
187{
188    USER_PANIC("NYI");
189}
190
191/**
192 * \brief Get the region corresponding to the given virtual address
193 *
194 * \param addr  The virtual address
195 */
196struct vregion* vspace_get_region(struct vspace *vspace, const void *addr)
197{
198    lvaddr_t lvaddr = (lvaddr_t)addr;
199    genvaddr_t genvaddr = vspace_lvaddr_to_genvaddr(lvaddr);
200
201    struct vregion *walk = vspace->head;
202    while (walk) {
203        if (walk->base <= genvaddr &&
204            walk->base + walk->size > genvaddr) {
205            return walk;
206        }
207        walk = walk->next;
208    }
209
210    return NULL;
211}
212
213/**
214 * \brief Page fault handler
215 *
216 * \param point The vspace page fault occured in
217 * \param addr  The faulting address
218 * \param type  The fault type
219 *
220 * Lookup the appropriate vregion and forward the fault to it
221 */
222errval_t vspace_pagefault_handler(struct vspace *vspace, lvaddr_t lvaddr,
223                                  vm_fault_type_t type)
224{
225    errval_t err;
226
227    genvaddr_t genvaddr =
228        vspace_layout_lvaddr_to_genvaddr(&vspace->layout, lvaddr);
229
230    struct vregion *walk = vspace->head;
231    while(walk != NULL) {
232        genvaddr_t base = vregion_get_base_addr(walk);
233        genvaddr_t size = vregion_get_size(walk);
234        if (genvaddr >= base && genvaddr < base + size) {
235            err = vregion_pagefault_handler(walk, genvaddr, type);
236            if (err_is_fail(err)) {
237                return err_push(err, LIB_ERR_VREGION_PAGEFAULT_HANDLER);
238            }
239            return SYS_ERR_OK;
240        }
241        walk = walk->next;
242    }
243
244    return LIB_ERR_VSPACE_PAGEFAULT_ADDR_NOT_FOUND;
245}
246