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 <stdio.h>
14#include <stdbool.h>
15#include <assert.h>
16#include <sel4/sel4.h>
17#include <refos/refos.h>
18#include <refos/error.h>
19#include <refos/vmlayout.h>
20#include <refos-util/walloc.h>
21#include <refos-rpc/proc_client.h>
22#include <refos-rpc/proc_client_helper.h>
23#include <data_struct/cbpool.h>
24#include <data_struct/chash.h>
25#include <refos-util/dprintf.h>
26
27static walloc_state_t _walloc_state;
28
29/* ------------------------------ Walloc-list internal interface  --------------------------------*/
30
31static void
32walloc_list_init(walloc_state_t *ws, seL4_Word startAddr, seL4_Word endAddr)
33{
34    uint32_t sz = endAddr - startAddr;
35    if (startAddr % REFOS_PAGE_SIZE != 0) {
36        assert(!"walloc_init startAddr is not page aligned.");
37        return;
38    }
39    if (sz % REFOS_PAGE_SIZE != 0) {
40        assert(!"walloc_init size is not a multiple of PAGE_SIZE.");
41        return;
42    }
43
44    /* Initialise the bitmap pool which keeps track of allocated portions of vspace. */
45    ws->startAddr = startAddr;
46    ws->endAddr = endAddr;
47    ws->npages = sz / REFOS_PAGE_SIZE;
48    cbpool_init(&ws->pool, ws->npages);
49
50    /* Initialise the windows list. */
51    chash_init(&ws->windowCptrMap, WALLOC_WINDOW_CPTR_MAP_HASHSIZE);
52
53    ws->initialised = true;
54    ws->magic = WALLOC_MAGIC;
55}
56
57static void
58walloc_list_deinit(walloc_state_t *ws)
59{
60    if (!ws->initialised) return;
61    chash_release(&ws->windowCptrMap);
62    cbpool_release(&ws->pool);
63    ws->startAddr = 0;
64    ws->endAddr = 0;
65    ws->npages = 0;
66    ws->initialised = false;
67    ws->magic = 0;
68}
69
70static seL4_Word
71walloc_list_ext(walloc_state_t *ws, int npages, seL4_CPtr *window, uint32_t permission,
72                uint32_t flags)
73{
74    assert(ws->initialised && ws->magic == WALLOC_MAGIC);
75    if (!npages) return 0;
76
77    // Allocate window.
78    uint32_t startPage = cbpool_alloc(&ws->pool, npages);
79    if (startPage == CBPOOL_INVALID) {
80        printf("WARNING: walloc out of windows.\n");
81        return 0;
82    }
83    assert(startPage >= 0 && startPage < ws->npages);
84
85    // Calculate the allocated window region address.
86    uint32_t regionAddr = ws->startAddr + (startPage * REFOS_PAGE_SIZE);
87    assert(regionAddr % REFOS_PAGE_SIZE == 0);
88
89    // Allocate a window at this address.
90    seL4_CPtr windowCap = proc_create_mem_window_ext(regionAddr, npages * REFOS_PAGE_SIZE,
91            permission, flags);
92    if (windowCap == 0 || REFOS_GET_ERRNO() != ESUCCESS) {
93        cbpool_free(&ws->pool, startPage, npages);
94        printf("WARNING: walloc could not create memory window.\n");
95        assert(!"WARNING: walloc could not create procserv memory window.\n");
96        return 0;
97    }
98
99    // Book keep this allocated window cap.
100    int err = chash_set(&ws->windowCptrMap, startPage, (chash_item_t) windowCap);
101    assert(!err);
102    (void) err;
103
104    if (window != NULL) {
105        (*window) = windowCap;
106    }
107    return regionAddr;
108}
109
110static seL4_Word
111walloc_list(walloc_state_t *ws, int npages, seL4_CPtr *window)
112{
113    return walloc_ext(npages, window, PROC_WINDOW_PERMISSION_READWRITE, 0x0);
114}
115
116static uint32_t
117walloc_list_get_start_page(walloc_state_t *ws, seL4_Word vaddr)
118{
119    assert(ws->initialised && ws->magic == WALLOC_MAGIC);
120    assert(vaddr >= ws->startAddr && vaddr <= ws->endAddr);
121    uint32_t startPage = (vaddr - ws->startAddr) / REFOS_PAGE_SIZE;
122    assert(startPage >= 0 && startPage < ws->npages);
123    return startPage;
124}
125
126static seL4_CPtr
127walloc_list_get_window_at_vaddr(walloc_state_t *ws, seL4_Word vaddr)
128{
129    assert(ws->initialised && ws->magic == WALLOC_MAGIC);
130    seL4_CPtr windowCap = (seL4_CPtr)
131            chash_get(&ws->windowCptrMap, walloc_list_get_start_page(ws, vaddr));
132    return windowCap;
133}
134
135static void
136walloc_list_free(walloc_state_t *ws, uint32_t addr, int npages)
137{
138    assert(ws->initialised && ws->magic == WALLOC_MAGIC);
139    if (!npages) return;
140    cbpool_free(&ws->pool, walloc_list_get_start_page(ws, addr), npages);
141    seL4_CPtr windowCap = walloc_get_window_at_vaddr(addr);
142    if (windowCap) {
143        proc_delete_mem_window(windowCap);
144        seL4_CNode_Revoke(REFOS_CSPACE, windowCap, REFOS_CSPACE_DEPTH);
145        csfree_delete(windowCap);
146        chash_remove(&ws->windowCptrMap, walloc_list_get_start_page(ws, addr));
147    }
148}
149
150/* --------------------------- Userland simplified walloc interface  -----------------------------*/
151
152void
153walloc_init(seL4_Word startAddr, seL4_Word endAddr)
154{
155    walloc_list_init(&_walloc_state, startAddr, endAddr);
156}
157
158void
159walloc_deinit(void)
160{
161    walloc_list_deinit(&_walloc_state);
162}
163
164seL4_Word
165walloc(int npages, seL4_CPtr *window)
166{
167    return walloc_list(&_walloc_state, npages, window);
168}
169
170seL4_Word
171walloc_ext(int npages, seL4_CPtr *window, uint32_t permission, uint32_t flags)
172{
173    return walloc_list_ext(&_walloc_state, npages, window, permission, flags);
174}
175
176seL4_CPtr
177walloc_get_window_at_vaddr(seL4_Word vaddr)
178{
179    return walloc_list_get_window_at_vaddr(&_walloc_state, vaddr);
180}
181
182void
183walloc_free(uint32_t addr, int npages)
184{
185    walloc_list_free(&_walloc_state, addr, npages);
186}