1/*
2 * Copyright 2016, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(D61_BSD)
11 */
12
13#include "../../common.h"
14#include "../../state.h"
15#include "pagedir.h"
16#include <sel4utils/mapping.h>
17
18/*! @file
19    @brief Static page directory allocator.
20
21    This procserv module is responsible for allocation and deallocation of client kernel page
22    directory objects, and root cnode objects. Because these objects are really big, continually
23    allocating and re-allocating them could fail due to memory fragmentation.
24*/
25
26#define PD_ID_BASE 1
27
28void
29pd_init(struct pd_list *pdlist)
30{
31    assert(pdlist);
32
33    /* Initialise the allocation pool. */
34    dprintf("Initialising static Page Directory pool (sized %d)...\n", PD_MAX);
35    cpool_init(&pdlist->PDpool, PD_ID_BASE, PD_ID_BASE + PD_MAX);
36
37    /* Statically allocate the page directories. */
38    for (int i = 0; i < PD_MAX; i++) {
39
40        /* Allocate the kernel vspace_root. */
41        int error = vka_alloc_vspace_root(&procServ.vka, &pdlist->pd[i]);
42        if (error) {
43            ROS_ERROR("Failed to allocate vspace root. error %d\n", error);
44            assert(!"Procserv initialisation failed. Try lowering CONFIG_PROCSERV_MAX_VSPACES.");
45            return;
46        }
47        assert(pdlist->pd[i].cptr != 0);
48
49        /* If we are on mainline, we need to assign a kernel ASID. ASID Pools have been removed
50           from the newer experimental kernels. */
51        #ifndef CONFIG_KERNEL_STABLE
52        #ifndef CONFIG_X86_64
53            error = seL4_ARCH_ASIDPool_Assign(seL4_CapInitThreadASIDPool, pdlist->pd[i].cptr);
54            assert(error == seL4_NoError);
55        #endif
56        #endif
57
58        /* Allocate the kernel Root CNode object. */
59        error = vka_alloc_cnode_object(&procServ.vka, REFOS_CSPACE_RADIX, &pdlist->cnode[i]);
60        if (error) {
61            ROS_ERROR("Failed to allocate Root CNode. error %d\n", error);
62            assert(!"Procserv initialisation failed. Try lowering CONFIG_PROCSERV_MAX_VSPACES.");
63            return;
64        }
65        assert(pdlist->cnode[i].cptr != 0);
66    }
67}
68
69struct pd_info
70pd_assign(struct pd_list *pdlist)
71{
72    assert(pdlist);
73    struct pd_info info;
74    uint32_t pdID = cpool_alloc(&pdlist->PDpool);
75    if (!pdID) {
76        info.kpdObject = 0;
77        info.kcnodeObject = 0;
78        return info;
79    }
80    info.kpdObject = pdlist->pd[pdID - 1].cptr;
81    info.kcnodeObject = pdlist->cnode[pdID - 1].cptr;
82    return info;
83}
84
85void
86pd_free(struct pd_list *pdlist, seL4_CPtr pdPtr)
87{
88    assert(pdlist);
89
90    /* First, we loop through our list and find the associated vka object with this given cptr. */
91    int idx = -1;
92    for (int i = 0; i < PD_MAX; i++) {
93        if (pdlist->pd[i].cptr == pdPtr) {
94            idx = i;
95            break;
96        }
97    }
98
99    if (idx == -1) {
100        /* Could not find the given cptr. */
101        ROS_WARNING("pd_free failed: no such page directory cptr %d\n", pdPtr);
102        return;
103    }
104
105    if (cpool_check(&pdlist->PDpool, idx + 1)) {
106        /* Already free. */
107        ROS_WARNING("pd_free failed: page directory cptr %d is already free.\n", pdPtr);
108        return;
109    }
110
111    /* Delete all derived caps from this PD. */
112    cspacepath_t cpath;
113    vka_cspace_make_path(&procServ.vka, pdPtr, &cpath);
114    vka_cnode_revoke(&cpath);
115
116    /* Delete this PD's associated root CNode. */
117    vka_cspace_make_path(&procServ.vka, pdlist->cnode[idx].cptr, &cpath);
118    vka_cnode_revoke(&cpath);
119    vka_cnode_delete(&cpath);
120    vka_free_object(&procServ.vka, &pdlist->cnode[idx]);
121
122    /* Allocate a new kernel Root CNode object. */
123    int error = vka_alloc_cnode_object(&procServ.vka, REFOS_CSPACE_RADIX, &pdlist->cnode[idx]);
124    if (error) {
125        ROS_ERROR("Failed to re-allocate Root CNode. error %d\n", error);
126        return;
127    }
128
129    /* Put it back into the allocation pool. */
130    cpool_free(&pdlist->PDpool, idx + 1);
131}
132