1/**
2 * \file
3 * \brief
4 */
5
6/*
7 * Copyright (c) 2010-2013, 2016 ETH Zurich.
8 * Copyright (c) 2014, HP Labs.
9 * All rights reserved.
10 *
11 * This file is distributed under the terms in the attached LICENSE file.
12 * If you do not find this file, copies can be found by writing to:
13 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
14 */
15
16#include <kernel.h>
17#include <dispatch.h>
18#include <target/x86_32/paging_kernel_target.h>
19#include <target/x86_32/offsets_target.h>
20#include <paging_kernel_arch.h>
21#include <mdb/mdb_tree.h>
22#include <string.h>
23#include <cap_predicates.h>
24#include <paging_generic.h>
25
26#ifdef CONFIG_PAE
27/// Map within a x86_32 pdpt
28static errval_t x86_32_pdpt(struct capability *dest, cslot_t slot,
29                            struct capability * src, uintptr_t flags,
30                            uintptr_t offset, uintptr_t pte_count,
31                            struct cte *mapping_cte)
32{
33    if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
34        return SYS_ERR_VNODE_SLOT_INVALID;
35    }
36
37    if (pte_count > 1) { // disallow multiple pdpt mappings at a time
38        return SYS_ERR_VM_MAP_SIZE;
39    }
40
41    if (src->type != ObjType_VNode_x86_32_pdir) { // Right mapping
42        return SYS_ERR_WRONG_MAPPING;
43    }
44
45    if(slot >= X86_32_PDPTE_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here
46        return SYS_ERR_VNODE_SLOT_RESERVED;
47    }
48
49    // Destination
50    genpaddr_t dest_gp   = dest->u.vnode_x86_32_pdpt.base;
51    lpaddr_t dest_lp     = gen_phys_to_local_phys(dest_gp);
52    lvaddr_t dest_lv     = local_phys_to_mem(dest_lp);
53    union x86_32_pdpte_entry *entry =
54        (union x86_32_pdpte_entry *)dest_lv + slot;
55
56    // Set metadata
57    create_mapping_cap(mapping_cte, src,
58                       dest_lp + slot * sizeof(union x86_32_pdpte_entry),
59                       pte_count);
60
61    // Source
62    genpaddr_t src_gp   = src->u.vnode_x86_32_pdir.base;
63    lpaddr_t src_lp     = gen_phys_to_local_phys(src_gp);
64    paging_x86_32_map_pdpte(entry, src_lp);
65    paging_x86_32_context_switch(dcb_current->vspace); // To flush TLB
66
67    return SYS_ERR_OK;
68}
69#endif
70
71/// Map within a x86_32 pdir
72static errval_t x86_32_pdir(struct capability *dest, cslot_t slot,
73                            struct capability * src, uintptr_t flags,
74                            uintptr_t offset, uintptr_t pte_count,
75                            struct cte *mapping_cte)
76{
77    //printf("x86_32_pdir\n");
78    if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
79        return SYS_ERR_VNODE_SLOT_INVALID;
80    }
81
82    if (slot + pte_count > X86_32_PTABLE_SIZE) {
83        // check that mapping fits page directory
84        return SYS_ERR_VM_MAP_SIZE;
85    }
86
87#ifndef CONFIG_PAE
88    if(slot >= X86_32_PDIR_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here
89        return SYS_ERR_VNODE_SLOT_RESERVED;
90    }
91#endif
92
93    // large page code
94    if(src->type == ObjType_Frame || src->type == ObjType_DevFrame)
95    {
96        cslot_t last_slot = slot + pte_count;
97
98        // check offset within frame
99        if (offset + pte_count * X86_32_LARGE_PAGE_SIZE > get_size(src)) {
100            return SYS_ERR_FRAME_OFFSET_INVALID;
101        }
102
103        /* Calculate page access protection flags */
104        // Get frame cap rights
105        paging_x86_32_flags_t flags_large =
106            paging_x86_32_cap_to_page_flags(src->rights);
107        // Mask with provided access rights mask
108        flags_large = paging_x86_32_mask_attrs(flags_large, X86_32_PTABLE_ACCESS(flags));
109        // Add additional arch-specific flags
110        flags_large |= X86_32_PTABLE_FLAGS(flags);
111        // Unconditionally mark the page present
112        flags_large |= X86_32_PTABLE_PRESENT;
113
114        // Convert destination base address
115        genpaddr_t dest_gp   = get_address(dest);
116        lpaddr_t dest_lp     = gen_phys_to_local_phys(dest_gp);
117        lvaddr_t dest_lv     = local_phys_to_mem(dest_lp);
118        // Convert source base address
119        genpaddr_t src_gp   = get_address(src);
120        lpaddr_t src_lp     = gen_phys_to_local_phys(src_gp);
121        // Set metadata
122        create_mapping_cap(mapping_cte, src,
123                           dest_lp + slot * sizeof(union x86_32_ptable_entry),
124                           offset,
125                           pte_count);
126
127        for (; slot < last_slot; slot++, offset += X86_32_LARGE_PAGE_SIZE) {
128            union x86_32_ptable_entry *entry =
129                (union x86_32_ptable_entry *)dest_lv + slot;
130
131            /* FIXME: Flush TLB if the page is already present
132             * in the meantime, since we don't do this, we just assert that
133             * we never reuse a VA mapping */
134            if (X86_32_IS_PRESENT(entry)) {
135                printf("Trying to map into an already present page NYI.\n");
136                return SYS_ERR_VNODE_SLOT_INUSE;
137            }
138
139            // Carry out the page mapping
140            paging_x86_32_map_large(entry, src_lp + offset, flags_large);
141        }
142
143        return SYS_ERR_OK;
144    }
145
146    if (src->type != ObjType_VNode_x86_32_ptable) { // Right mapping
147        return SYS_ERR_WRONG_MAPPING;
148    }
149
150    // Destination
151    genpaddr_t dest_gp   = dest->u.vnode_x86_32_pdir.base;
152    lpaddr_t dest_lp     = gen_phys_to_local_phys(dest_gp);
153    lvaddr_t dest_lv     = local_phys_to_mem(dest_lp);
154    union x86_32_pdir_entry *entry =
155        (union x86_32_pdir_entry *)dest_lv + slot;
156
157    // Set metadata
158    create_mapping_cap(mapping_cte, src,
159                       dest_lp + slot * sizeof(union x86_32_pdir_entry),
160                       pte_count);
161
162
163    // Source
164    // XXX: offset is ignored
165    genpaddr_t src_gp   = src->u.vnode_x86_32_pdir.base;
166    lpaddr_t src_lp     = gen_phys_to_local_phys(src_gp);
167    paging_x86_32_map_table(entry, src_lp);
168
169    return SYS_ERR_OK;
170}
171
172/// Map within a x86_32 ptable
173static errval_t x86_32_ptable(struct capability *dest, cslot_t slot,
174                              struct capability * src, uintptr_t uflags,
175                              uintptr_t offset, uintptr_t pte_count,
176                              struct cte *mapping_cte)
177{
178    //printf("x86_32_ptable\n");
179    if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
180        return SYS_ERR_VNODE_SLOT_INVALID;
181    }
182
183    cslot_t last_slot = slot + pte_count;
184
185    if (last_slot > X86_32_PTABLE_SIZE) {
186        printf("slot = %"PRIuCSLOT", last_slot = %"PRIuCSLOT", PTABLE_SIZE = %d\n", slot, last_slot, X86_32_PTABLE_SIZE);
187        return SYS_ERR_VM_MAP_SIZE;
188    }
189
190    if (src->type != ObjType_Frame &&
191        src->type != ObjType_DevFrame) { // Right mapping
192        return SYS_ERR_WRONG_MAPPING;
193    }
194
195    // check offset within frame
196    if (offset + pte_count * X86_32_BASE_PAGE_SIZE > get_size(src)) {
197        return SYS_ERR_FRAME_OFFSET_INVALID;
198    }
199
200    /* Calculate page access protection flags */
201    // Get frame cap rights
202    paging_x86_32_flags_t flags =
203        paging_x86_32_cap_to_page_flags(src->rights);
204    // Mask with provided access rights mask
205    flags = paging_x86_32_mask_attrs(flags, X86_32_PTABLE_ACCESS(uflags));
206    // Add additional arch-specific flags
207    flags |= X86_32_PTABLE_FLAGS(uflags);
208    // Unconditionally mark the page present
209    flags |= X86_32_PTABLE_PRESENT;
210
211    // Convert destination base address
212    genpaddr_t dest_gp   = get_address(dest);
213    lpaddr_t dest_lp     = gen_phys_to_local_phys(dest_gp);
214    lvaddr_t dest_lv     = local_phys_to_mem(dest_lp);
215    // Convert source base address
216    genpaddr_t src_gp   = get_address(src);
217    lpaddr_t src_lp     = gen_phys_to_local_phys(src_gp);
218    // Set metadata
219    create_mapping_cap(mapping_cte, src,
220                       dest_lp + slot * sizeof(union x86_32_ptable_entry),
221                       offset,
222                       pte_count);
223
224
225    for (; slot < last_slot; slot++, offset += X86_32_BASE_PAGE_SIZE) {
226        union x86_32_ptable_entry *entry =
227            (union x86_32_ptable_entry *)dest_lv + slot;
228
229        /* FIXME: Flush TLB if the page is already present
230         * in the meantime, since we don't do this, we just assert that
231         * we never reuse a VA mapping */
232        if (X86_32_IS_PRESENT(entry)) {
233            panic("Trying to map into an already present page NYI.");
234        }
235
236        // Carry out the page mapping
237        paging_x86_32_map(entry, src_lp + offset, flags);
238    }
239
240    return SYS_ERR_OK;
241}
242
243typedef errval_t (*mapping_handler_t)(struct capability *dest_cap,
244                                      cslot_t dest_slot,
245                                      struct capability *src_cap,
246                                      uintptr_t flags, uintptr_t offset,
247                                      uintptr_t pte_count,
248                                      struct cte *mapping_cte);
249
250/// Dispatcher table for the type of mapping to create
251static mapping_handler_t handler[ObjType_Num] = {
252#ifdef CONFIG_PAE
253    [ObjType_VNode_x86_32_pdpt]   = x86_32_pdpt,
254#endif
255    [ObjType_VNode_x86_32_pdir]   = x86_32_pdir,
256    [ObjType_VNode_x86_32_ptable] = x86_32_ptable,
257};
258
259/// Create page mappings
260errval_t caps_copy_to_vnode(struct cte *dest_vnode_cte, cslot_t dest_slot,
261                            struct cte *src_cte, uintptr_t flags,
262                            uintptr_t offset, uintptr_t pte_count,
263                            struct cte *mapping_cte)
264{
265    assert(type_is_vnode(dest_vnode_cte->cap.type));
266    assert(mapping_cte->cap.type == ObjType_Null);
267
268    struct capability *src_cap  = &src_cte->cap;
269    struct capability *dest_cap = &dest_vnode_cte->cap;
270    mapping_handler_t handler_func = handler[dest_cap->type];
271
272    assert(handler_func != NULL);
273
274    cslot_t last_slot = dest_slot + pte_count;
275
276    // TODO: PAE
277    if (last_slot > X86_32_PTABLE_SIZE) {
278        // requested map overlaps leaf page table
279        debug(SUBSYS_CAPS,
280                "caps_copy_to_vnode: requested mapping spans multiple leaf page tables\n");
281        return SYS_ERR_VM_RETRY_SINGLE;
282    }
283
284    errval_t r = handler_func(dest_cap, dest_slot, src_cap, flags, offset,
285                              pte_count, mapping_cte);
286    if (err_is_fail(r)) {
287        assert(mapping_cte->cap.type == ObjType_Null);
288        debug(SUBSYS_PAGING, "caps_copy_to_vnode: handler func returned %d\n", r);
289        return r;
290    }
291
292    /* insert mapping cap into mdb */
293    errval_t err = mdb_insert(mapping_cte);
294    if (err_is_fail(err)) {
295        printk(LOG_ERR, "%s: mdb_insert: %"PRIuERRV"\n", __FUNCTION__, err);
296    }
297
298    TRACE_CAP_MSG("created", mapping_cte);
299
300    return err;
301}
302
303size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages)
304{
305    size_t unmapped_pages = 0;
306    union x86_32_ptable_entry *ptentry = (union x86_32_ptable_entry *)pt + slot;
307    for (int i = 0; i < num_pages; i++) {
308        ptentry++->raw = 0;
309        unmapped_pages++;
310    }
311    return unmapped_pages;
312}
313
314errval_t page_mappings_modify_flags(struct capability *mapping, size_t offset,
315                                    size_t pages, size_t mflags, genvaddr_t va_hint)
316{
317    assert(type_is_mapping(mapping->type));
318    struct Frame_Mapping *info = &mapping->u.frame_mapping;
319
320    /* Calculate page access protection flags */
321    // Get frame cap rights
322    paging_x86_32_flags_t flags =
323        paging_x86_32_cap_to_page_flags(info->cap->rights);
324    // Mask with provided access rights mask
325    flags = paging_x86_32_mask_attrs(flags, X86_32_PTABLE_ACCESS(mflags));
326    // Add additional arch-specific flags
327    flags |= X86_32_PTABLE_FLAGS(mflags);
328    // Unconditionally mark the page present
329    flags |= X86_32_PTABLE_PRESENT;
330
331    // check arguments
332    if (offset >= X86_32_PTABLE_SIZE) { // Within pagetable
333        return SYS_ERR_VNODE_SLOT_INVALID;
334    }
335    if (offset + pages > X86_32_PTABLE_SIZE) { // mapping size ok
336        return SYS_ERR_VM_MAP_SIZE;
337    }
338
339    /* Calculate location of page table entries we need to modify */
340    lvaddr_t base = local_phys_to_mem(info->pte) +
341        offset * sizeof(union x86_32_ptable_entry);
342
343    // get pt cap to figure out page size
344    struct cte *leaf_pt;
345    errval_t err;
346    err = mdb_find_cap_for_address(info->pte, &leaf_pt);
347    if (err_is_fail(err)) {
348        return err;
349    }
350
351    size_t pagesize = BASE_PAGE_SIZE;
352    switch(leaf_pt->cap.type) {
353        case ObjType_VNode_x86_32_ptable :
354            for (int i = 0; i < pages; i++) {
355                union x86_32_ptable_entry *entry =
356                    (union x86_32_ptable_entry *)base + i;
357                paging_x86_32_modify_flags(entry, flags);
358            }
359            break;
360        case ObjType_VNode_x86_32_pdir :
361            for (int i = 0; i < pages; i++) {
362                union x86_32_ptable_entry *entry =
363                    (union x86_32_ptable_entry *)base + i;
364                paging_x86_32_modify_flags_large(entry, flags);
365            }
366            pagesize = LARGE_PAGE_SIZE;
367            break;
368        default:
369            return SYS_ERR_WRONG_MAPPING;
370    }
371
372    if (va_hint != 0 && va_hint > BASE_PAGE_SIZE) {
373        // use as direct hint
374        // invlpg should work for large/huge pages
375        for (int i = 0; i < pages; i++) {
376            do_one_tlb_flush(va_hint + i * pagesize);
377        }
378    } else {
379        /* do full TLB flush */
380        do_full_tlb_flush();
381    }
382    return SYS_ERR_OK;
383}
384
385void paging_dump_tables(struct dcb *dispatcher)
386{
387    if (dispatcher->vspace > X86_32_PADDR_SPACE_LIMIT) {
388        printk(LOG_ERR, "dispatcher->vspace = 0x%"PRIxLPADDR": too high!\n" ,
389               dispatcher->vspace);
390        return;
391    }
392    lvaddr_t root_pt = local_phys_to_mem(dispatcher->vspace);
393
394#ifdef CONFIG_PAE
395    // loop over pdpt entries
396    for (int pdir_index = 0; pdir_index < X86_32_PDPTE_SIZE; pdir_index++) {
397        // get pdir
398        union x86_32_pdpte_entry *pdir = (union x86_32_pdpte_entry *)root_pt + pdir_index;
399        if (!pdir->raw) { continue; }
400        genpaddr_t pdir_gp = pdir->d.base_addr << BASE_PAGE_BITS;
401        lvaddr_t pdir_lv = local_phys_to_mem(gen_phys_to_local_phys(pdir_gp));
402#else
403        int pdir_index = 0;
404        lvaddr_t pdir_lv = root_pt;
405#endif
406
407        // only go to 512 because upper half of address space is kernel space
408        // (1:1 mapped)
409        // TODO: figure out what we need to do here for PAE
410        for (int ptable_index = 0; ptable_index < 512; ptable_index++) {
411            // get ptable
412            union x86_32_pdir_entry *ptable = (union x86_32_pdir_entry *)pdir_lv + ptable_index;
413            union x86_32_ptable_entry *large = (union x86_32_ptable_entry *)ptable;
414            if (!ptable->raw) { continue; }
415            if (large->large.always1) {
416                // large page
417                genpaddr_t paddr = large->large.base_addr << X86_32_LARGE_PAGE_BITS;
418                printf("%d.%d: 0x%"PRIxGENPADDR"\n", pdir_index,
419                        ptable_index, paddr);
420            }
421            genpaddr_t ptable_gp = ptable->d.base_addr << BASE_PAGE_BITS;
422            lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp));
423
424            for (int entry = 0; entry < X86_32_PTABLE_SIZE; entry++) {
425                union x86_32_ptable_entry *e =
426                    (union x86_32_ptable_entry *)ptable_lv + entry;
427                genpaddr_t paddr = (genpaddr_t)e->base.base_addr << BASE_PAGE_BITS;
428                if (!paddr) {
429                    continue;
430                }
431                printf("%d.%d.%d: 0x%"PRIxGENPADDR"\n", pdir_index, ptable_index, entry, paddr);
432            }
433        }
434#ifdef CONFIG_PAE
435    } // endfor PDPT entries
436#endif
437}
438