1/*
2 * Copyright (c) 2015, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdio.h>
11#include <barrelfish/ram_alloc.h>
12#include "nestedpaging.h"
13
14// allocate real vnode
15static errval_t allocate_vnode(enum objtype type, struct capref *vnode, void **mapped)
16{
17    errval_t err;
18    struct capref vnode_ram, vnode_cap;
19    err = ram_alloc(&vnode_ram, BASE_PAGE_BITS);
20    if (err_is_fail(err)) {
21        return err;
22    }
23    err = slot_alloc(&vnode_cap);
24    if (err_is_fail(err)) {
25        return err;
26    }
27    assert(type == ObjType_VNode_x86_64_pdpt ||
28           type == ObjType_VNode_x86_64_pdir ||
29           type == ObjType_VNode_x86_64_ptable);
30    err = cap_retype(vnode_cap, vnode_ram, 0, type, 0, 1);
31    if (err_is_fail(err)) {
32        return err;
33    }
34    err = cap_destroy(vnode_ram);
35    if (err_is_fail(err)) {
36        return err;
37    }
38    *vnode = vnode_cap;
39
40    struct capref vnode_map;
41    err = slot_alloc(&vnode_map);
42    if (err_is_fail(err)) {
43        return err;
44    }
45    err = cap_copy(vnode_map, vnode_cap);
46    if (err_is_fail(err)) {
47        return err;
48    }
49
50    assert(mapped);
51    err = vspace_map_one_frame_attr(mapped, BASE_PAGE_SIZE, vnode_map,
52            VREGION_FLAGS_READ_WRITE, NULL, NULL);
53    if (err_is_fail(err)) {
54        return err;
55    }
56
57    return SYS_ERR_OK;
58}
59
60// allocate 8kB
61// first 4kB: HW table
62// second 4kB: SW table
63static errval_t allocate_user_vnode(struct capref *vnode, void **mapped)
64{
65    errval_t err;
66    struct capref vnode_cap;
67    err = frame_alloc(&vnode_cap, 2*BASE_PAGE_BITS);
68    if (err_is_fail(err)) {
69        return err;
70    }
71    *vnode = vnode_cap;
72    assert(mapped);
73    err = vspace_map_one_frame_attr(mapped, BASE_PAGE_SIZE, vnode_cap,
74            VREGION_FLAGS_READ_WRITE, NULL, NULL);
75    if (err_is_fail(err)) {
76        return err;
77    }
78
79    return SYS_ERR_OK;
80}
81
82
83extern errval_t vspace_add_vregion(struct vspace *vspace, struct vregion *region);
84errval_t install_user_managed_pdpt(struct pml4_entry *retentry)
85{
86    errval_t err;
87    assert(retentry);
88    printf("Installing our own page tables\n");
89    struct pmap *p = get_current_pmap();
90    struct memobj m;
91    // full pml4 entry
92    m.size = 512ULL * 1024 * 1024 * 1024;
93    genvaddr_t base;
94    err = p->f.determine_addr(p, &m, m.size, &base);
95    if (err_is_fail(err)) {
96        return err;
97    }
98    printf("base: %"PRIxGENVADDR"\n", base);
99
100    struct vregion ours;
101    ours.base = base;
102    ours.size = m.size;
103    vspace_add_vregion(get_current_vspace(), &ours);
104
105    struct capref pdpt_cap;
106    void *ptable;
107    err = allocate_vnode(ObjType_VNode_x86_64_pdpt, &pdpt_cap, &ptable);
108    if (err_is_fail(err)) {
109        return err;
110    }
111    printf("pdpt mapped at %p\n", ptable);
112
113    struct capref pml4 = (struct capref) {
114        .cnode = cnode_page,
115        .slot = 0
116    };
117    size_t pml4e = X86_64_PML4_BASE(base);
118    printf("our pml4e is: %zu\n", pml4e);
119    err = vnode_map(pml4, pdpt_cap, pml4e, PTABLE_ACCESS_DEFAULT, 0, 1);
120    if (err_is_fail(err)) {
121        return err;
122    }
123
124    // fill out retentry struct
125    retentry->addr    = ptable;
126    retentry->cap     = pdpt_cap;
127    retentry->entry   = pml4e;
128
129    struct capref swt;
130    size_t retsize;
131    err = frame_alloc(&swt, BASE_PAGE_SIZE, &retsize);
132    if (err_is_fail(err)) {
133        return err;
134    }
135    err = vspace_map_one_frame((void**)&retentry->swtable, BASE_PAGE_SIZE,
136            swt, NULL, NULL);
137    if (err_is_fail(err)) {
138        return err;
139    }
140
141    return SYS_ERR_OK;
142}
143
144errval_t user_managed_map_frame(struct pml4_entry *pdpt,
145                                genvaddr_t vaddr,
146                                struct capref frame,
147                                vregion_flags_t flags)
148{
149    assert(pdpt);
150    errval_t err;
151
152    size_t pdpte = X86_64_PDPT_BASE(vaddr);
153    size_t pdire = X86_64_PDIR_BASE(vaddr);
154    size_t pte   = X86_64_PTABLE_BASE(vaddr);
155
156    printf("mapping at pdpte %zu, pdire %zu, pte %zu\n",
157            pdpte, pdire, pte);
158
159    struct frame_identity fi;
160    err = frame_identify(frame, &fi);
161
162    size_t fsize   = (1ULL << fi.bits);
163    size_t npages  = fsize / BASE_PAGE_SIZE;
164    size_t npdires = fsize / LARGE_PAGE_SIZE;
165    size_t npdptes = fsize / HUGE_PAGE_SIZE;
166    if (npdires == 0) { npdires = 1; }
167    if (npdptes == 0) { npdptes = 1; }
168    printf("mapping %zu pages, %zu pdires, %zu pdptes\n",
169            npages, npdires, npdptes);
170
171    paging_x86_64_flags_t pmap_flags = vregion_to_pmap(flags);
172
173    assert(npdptes == 1);
174    assert(npdires <= 512);
175
176    struct frame_identity ti;
177
178    union x86_64_pdir_entry *pdir = NULL;
179    if (!pdpt->swtable[pdpte]) {
180        // need to get pdir
181        struct capref pdir_cap;
182        void *vpdir;
183        err = allocate_user_vnode(&pdir_cap, &vpdir);
184        if (err_is_fail(err)) {
185            return err;
186        }
187        err = frame_identify(pdir_cap, &ti);
188        assert(err_is_ok(err));
189        union x86_64_pdir_entry *pdpt_ptr = pdpt->addr;
190        // map new pdir in hw pdpt
191        paging_x86_64_map_table(&pdpt_ptr[pdpte], ti.base);
192        // map new pdir in sw pdpt
193        pdpt->swtable[pdpte] = vpdir;
194    } else {
195        assert(pdpt->swtable[pdpte]);
196        pdir = pdpt->swtable[pdpte];
197    }
198    assert(pdir);
199    union x86_64_pdir_entry *swpdir = pdir + PTABLE_SIZE;
200
201    genpaddr_t offset = 0;
202    for (int i = pdire; i < pdire + npdires; i++) {
203        void *pt = NULL;
204        if (i == 0 || i == pdire + npdires - 1) {
205            // first/last pt
206            if (!swpdir[i]) {
207                // get ptable
208                struct capref pt_cap;
209                void *vpt;
210                err = allocate_user_vnode(&pt_cap, &vpt);
211                if (err_is_fail(err)) {
212                    return err;
213                }
214                err = frame_identify(pt_cap, &ti);
215                assert(err_is_ok(err));
216                // map new ptable in hw pdir
217                paging_x86_64_map_table(&pdir[i], ti.base);
218                // map new ptable in sw pdir
219                swpdir[i] = vpt;
220            } else {
221                assert(swpdir[i]);
222                pt = swpdir[i];
223            }
224            assert(pt);
225
226            size_t first_e  = i ? 0 : pte;
227            assert(first_e < PTABLE_SIZE);
228            size_t map_here = npages < PTABLE_SIZE ? npages : PTABLE_SIZE;
229            for (int j = first_e; j < first_e + map_here; j++) {
230                paging_x86_64_map(&pt[j], fi.base + offset, pmap_flags);
231                offset += BASE_PAGE_SIZE;
232                npages -= 1;
233            }
234        } else {
235            // full leaf: map as 2M page
236            assert(swpdir[i] == NULL);
237            union x86_64_ptable_entry *pde = (union x86_64_ptable_entry *)swpdir[i];
238            paging_x86_64_map_large(pde, fi.base + offset, pmap_flags);
239            offset += LARGE_PAGE_SIZE;
240            npages -= PTABLE_SIZE;
241        }
242    }
243
244    return SYS_ERR_OK;
245}
246