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 <sel4utils/vspace.h>
14
15#include "window.h"
16#include "../../badge.h"
17#include "../../state.h"
18
19#include "../addrspace/vspace.h"
20#include "../process/pid.h"
21#include "../process/process.h"
22
23/*! @file
24    @brief Manages and keeps track of memory windows. */
25
26#define W_INITIAL_SIZE 4
27
28/*! @brief Internal helper function to switch a window between modes.
29
30    It will first release all the previous stored mode related objects, and if the window wasn't
31    empty to start with, unmap the vspace range (so new window data isn't munged with old mapped
32    pages). Then it sets the mode variable of the window. This means that using this helper function
33    to set the mode to W_MODE_EMPTY will effectively delete all the mode-related objects and unmap
34    old stuff in the window.
35
36    @param window The window to switch mode for. (No ownership)
37    @param mode The new mode to switch to.
38*/
39static void
40window_switch_mode(struct w_window* window, enum w_window_mode mode)
41{
42    /* Clean up all windoe mode states. */
43    assert(window && window->magic == W_MAGIC);
44
45    /* Free the pager capability. */
46    if (window->pager.capPtr) {
47        vka_cnode_revoke(&window->pager);
48        vka_cnode_delete(&window->pager);
49        vka_cspace_free(&procServ.vka, window->pager.capPtr);
50        window->pager.capPtr = 0;
51        window->pagerPID = PID_NULL;
52    }
53
54    /* Unreference the associated dataspace. */
55    if (window->ramDataspace) {
56        ram_dspace_unref(window->ramDataspace->parentList, window->ramDataspace->ID);
57        window->ramDataspace = NULL;
58        window->ramDataspaceOffset = (vaddr_t) 0;
59    }
60
61    if (window->mode != W_MODE_EMPTY) {
62        /* There was something previous here. So we must first unmap this window. */
63        if (window->parentList == &procServ.windowList && window->clientOwnerPID != PID_NULL) {
64            /* Get owner client PCB to access its vspace. */
65            struct proc_pcb* clientPCB = pid_get_pcb(&procServ.PIDList,
66                                                     window->clientOwnerPID);
67            if (clientPCB) {
68                vs_unmap_window(&clientPCB->vspace, window->wID);
69            }
70        }
71    }
72    window->mode = mode;
73}
74
75/*! @brief Window OAT creation callback function.
76
77    This callback function is called by the OAT allocation helper library in <data_struct/coat.h>,
78    in order to create window objects.
79*/
80static cvector_item_t
81window_oat_create(coat_t *oat, int id, uint32_t arg[COAT_ARGS])
82{
83    struct w_window *nw = kmalloc(sizeof(struct w_window));
84    if (!nw) {
85        ROS_ERROR("window_oat_create out of memory!");
86        return NULL;
87    }
88    memset(nw, 0, sizeof(struct w_window));
89    nw->magic = W_MAGIC;
90    nw->wID = id;
91    nw->parentList = (struct w_list*) oat;
92    nw->pagerPID = PID_NULL;
93    assert(nw->parentList->magic == W_LIST_MAGIC);
94
95    /* Mint the badged capability representing this window. */
96    nw->capability = procserv_mint_badge(W_BADGE_BASE + id);
97    if (!nw->capability.capPtr) {
98        ROS_ERROR("window_oat_create could not mint cap!");
99        free(nw);
100        return NULL;
101    }
102    return (cvector_item_t) nw;
103}
104
105/*! @brief Window OAT deletion callback function.
106
107    This callback function is called by the OAT allocation helper library in <data_struct/coat.h>,
108    in order to delete window objects previously created by window_oat_create().
109*/
110static void
111window_oat_delete(coat_t *oat, cvector_item_t *obj)
112{
113    struct w_window *window = (struct w_window *) obj;
114    assert(window);
115    assert(window->magic == W_MAGIC);
116
117    /* Clean up window mode state. */
118    window_switch_mode(window, W_MODE_EMPTY);
119
120    /* Free the reservation. */
121    if (window->reservation.res != NULL) {
122        assert(window->vspace);
123        vspace_free_reservation(window->vspace, window->reservation);
124        window->reservation.res = NULL;
125    }
126
127    /* Free the capability. */
128    if (window->capability.capPtr) {
129        vka_cnode_revoke(&window->capability);
130        vka_cnode_delete(&window->capability);
131        vka_cspace_free(&procServ.vka, window->capability.capPtr);
132    }
133
134    /* Free the actual window structure. */
135    memset(window, 0, sizeof(struct w_window));
136    kfree(window);
137}
138
139/* --------------------------------------- Window functions ------------------------------------- */
140
141void
142w_init(struct w_list *wlist)
143{
144    assert(wlist);
145    dprintf("Initialising window allocation table (max %d windows).\n", W_MAX_WINDOWS);
146
147    /* Configure the object allocation table creation / deletion callback func pointers. */
148    wlist->windows.oat_expand = NULL;
149    wlist->windows.oat_create = window_oat_create;
150    wlist->windows.oat_delete = window_oat_delete;
151    wlist->magic = W_LIST_MAGIC;
152
153    /* Initialise the allocation table. */
154    coat_init(&wlist->windows, 1, W_MAX_WINDOWS);
155}
156
157void
158w_deinit(struct w_list *wlist)
159{
160    assert(wlist);
161    coat_release(&wlist->windows);
162}
163
164struct w_window*
165w_create_window(struct w_list *wlist, vaddr_t size, int ownerPID, seL4_Word permissions,
166    vspace_t *vspace, reservation_t reservation, bool cacheable)
167{
168    assert(wlist);
169    uint32_t arg[COAT_ARGS];
170    struct w_window* w = NULL;
171
172    /* Allocate the window ID. */
173    int ID = coat_alloc(&wlist->windows, arg, (cvector_item_t *) &w);
174    if (ID == W_INVALID_WINID) {
175        ROS_ERROR("Could not allocate window.");
176        return NULL;
177    }
178
179    /* Fill in the structure. */
180    assert(w != NULL && w->magic == W_MAGIC);
181    w->clientOwnerPID = ownerPID;
182    w->size = size;
183    w->mode = W_MODE_EMPTY;
184    w->permissions = permissions;
185    w->vspace = vspace;
186    w->reservation = reservation;
187    w->cacheable = cacheable;
188    return w;
189}
190
191int
192w_delete_window(struct w_list *wlist, int windowID)
193{
194    coat_free(&wlist->windows, windowID);
195    return ESUCCESS;
196}
197
198struct w_window*
199w_get_window(struct w_list *wlist, int windowID)
200{
201    if (windowID <= W_INVALID_WINID || windowID >= W_MAX_WINDOWS) {
202        /* Invalid ID. */
203        return NULL;
204    }
205    struct w_window* window = (struct w_window*) coat_get(&wlist->windows, windowID);
206    if (!window) {
207        /* No such window ID exists. */
208        return NULL;
209    }
210    assert(window->magic == W_MAGIC);
211    return window;
212}
213
214void
215w_set_pager_endpoint(struct w_window *window, cspacepath_t endpoint, uint32_t pid)
216{
217    assert(window && window->magic == W_MAGIC);
218    if (!endpoint.capPtr) {
219        window_switch_mode(window, W_MODE_EMPTY);
220        return;
221    }
222    window_switch_mode(window, W_MODE_PAGER);
223    window->pager = endpoint;
224    window->pagerPID = pid;
225}
226
227void
228w_set_anon_dspace(struct w_window *window, struct ram_dspace *dspace, vaddr_t offset)
229{
230    assert(window && window->magic == W_MAGIC);
231    if (!dspace) {
232        window_switch_mode(window, W_MODE_EMPTY);
233        return;
234    }
235    assert(dspace->magic = RAM_DATASPACE_MAGIC);
236    window_switch_mode(window, W_MODE_ANONYMOUS);
237    window->ramDataspace = dspace;
238    window->ramDataspaceOffset = offset;
239    ram_dspace_ref(dspace->parentList, dspace->ID);
240}
241
242void
243w_purge_dspace(struct w_list *wlist, struct ram_dspace *dspace)
244{
245    assert(wlist);
246    for (int i = 1; i < W_MAX_WINDOWS; i++) {
247        struct w_window *window = w_get_window(wlist, i);
248
249        if (window && window->ramDataspace) {
250            if (window->ramDataspace == dspace || (window->ramDataspace->parentList ==
251                    dspace->parentList && window->ramDataspace->ID == dspace->ID)) {
252                /* Set it back to empty. Not that this will unmap the window. */
253                window_switch_mode(window, W_MODE_EMPTY);
254            }
255        }
256    }
257}
258
259int
260w_resize_window(struct w_window *window, vaddr_t vaddr, vaddr_t size)
261{
262    assert(window && window->magic == W_MAGIC);
263    if (size == window->size) {
264        /* Nothing to do here. */
265        return ESUCCESS;
266    }
267
268    int error = sel4utils_move_resize_reservation(window->vspace, window->reservation,
269            (void*) vaddr, size);
270    if (error) {
271        return EINVALIDWINDOW;
272    }
273
274    dvprintf("window ID %d resized from size 0x%x to 0x%x\n", window->wID, window->size, size);
275    window->size = size;
276    return ESUCCESS;
277}
278
279/* -------------------------------- Window Association functions -------------------------------- */
280
281static int
282w_associate_compare(const void * a, const void * b)
283{
284    return ( ((struct w_associated_window*) a)->offset <
285             ((struct w_associated_window*) b)->offset ?
286            -1 : 1
287    );
288}
289
290/*! @brief Updates the window association list by sorting the base address of the window.
291    @param aw The window association list of a process.
292 */
293static void
294w_associate_update(struct w_associated_windowlist *aw)
295{
296    assert(aw);
297    qsort(aw->associated, aw->numIndex, sizeof(struct w_associated_window), w_associate_compare);
298    aw->updated = true;
299}
300
301static void
302w_associate_reserve(struct w_associated_windowlist *aw, int num) {
303    if (num < aw->associatedVectorSize) {
304        /* Nothing to do. */
305        return;
306    }
307    if (aw->associatedVectorSize == 0 || aw->associated == NULL) {
308        aw->associatedVectorSize = num;
309        if (aw->associatedVectorSize < W_INITIAL_SIZE) {
310            aw->associatedVectorSize = W_INITIAL_SIZE;
311        }
312        aw->associated = kmalloc(sizeof(struct w_associated_window) * aw->associatedVectorSize);
313        assert(aw->associated);
314        return;
315    }
316    aw->associatedVectorSize = (aw->associatedVectorSize * 2) + 1;
317    aw->associated = krealloc(aw->associated,
318            sizeof(struct w_associated_window) * aw->associatedVectorSize);
319    assert(aw->associated);
320}
321
322void
323w_associate_init(struct w_associated_windowlist *aw)
324{
325    aw->associated = NULL;
326    w_associate_clear(aw);
327}
328
329int
330w_associate(struct w_associated_windowlist *aw, int winID, vaddr_t offset, vaddr_t size)
331{
332    assert(aw);
333    assert(winID != W_INVALID_WINID);
334    assert(winID > 0 && winID < W_MAX_WINDOWS);
335    if (aw->numIndex >= W_MAX_ASSOCIATED_WINDOWS) {
336        return ENOMEM;
337    }
338    w_associate_reserve(aw, aw->numIndex + 1    );
339    aw->associated[aw->numIndex].winID = winID;
340    aw->associated[aw->numIndex].offset = offset;
341    aw->associated[aw->numIndex].size = size;
342    aw->numIndex++;
343    aw->updated = 0;
344    return ESUCCESS;
345}
346
347void
348w_associate_print(struct w_associated_windowlist *aw)
349{
350    assert(aw);
351    for (int i = 0; i < aw->numIndex; i++) {
352        dprintf("    ��� Associated window %d: winID %d addr 0x%x ��������� 0x%x, size 0x%x\n", i,
353                aw->associated[i].winID, aw->associated[i].offset,
354                aw->associated[i].offset + aw->associated[i].size,
355                aw->associated[i].size);
356    }
357}
358
359void
360w_unassociate(struct w_associated_windowlist *aw, int winID)
361{
362    assert(aw);
363    for (int i = 0; i < aw->numIndex; i++) {
364        if (aw->associated[i].winID == winID) {
365            aw->associated[i] = aw->associated[--aw->numIndex];
366        }
367    }
368    /* Removing something from a sorted list doesn't make it unsorted. */
369}
370
371void
372w_associate_clear(struct w_associated_windowlist *aw)
373{
374    assert(aw);
375    aw->numIndex = 0;
376    if (aw->associated != NULL) {
377        kfree(aw->associated);
378        aw->associated = NULL;
379    }
380    aw->associatedVectorSize = 0;
381    /* An empty list is sorted. */
382    aw->updated = true;
383    /* Reserve an initial few window spots. */
384    w_associate_reserve(aw, W_INITIAL_SIZE);
385}
386
387void
388w_associate_release_associated_all_windows(struct w_list *wlist, struct w_associated_windowlist *aw)
389{
390    assert(aw && wlist);
391    for (int i = 0; i < aw->numIndex; i++) {
392        /* Delete this window from the window list. */
393        w_delete_window(wlist, aw->associated[i].winID);
394    }
395    w_associate_clear(aw);
396}
397
398static inline char
399w_associate_window_contains(struct w_associated_window *w, vaddr_t addr)
400{
401    return (w->offset <= addr &&
402            w->offset + w->size > addr);
403}
404
405static int
406w_associate_find_index(struct w_associated_windowlist *aw, vaddr_t addr)
407{
408    assert(aw);
409
410    /* Re-sort the list if it's out of date. */
411    if (!aw->updated) {
412        w_associate_update(aw);
413        assert(aw->updated);
414    }
415
416    /* Binary search for the associated window. */
417
418    int startIndex = 0;
419    int endIndex = aw->numIndex;
420    int currentIndex = -1;
421    char found = false;
422
423    while (1) {
424        currentIndex = (startIndex + endIndex) / 2;
425
426        if (startIndex >= endIndex) {
427            break;
428        }
429
430        assert(currentIndex >= 0);
431        assert(currentIndex < aw->numIndex);
432
433        if (w_associate_window_contains(&aw->associated[currentIndex], addr)) {
434            /* We have found the correct window. */
435            found = true;
436            break;
437        } else if (aw->associated[currentIndex].offset <= addr) {
438            /* Guess is too small, search down the bigger half. */
439            startIndex = currentIndex + 1;
440        } else {
441            /* Guess is too large, search down the smaller half. */
442            endIndex = currentIndex;
443        }
444    }
445
446    return found ? currentIndex : (-1 - currentIndex);
447}
448
449struct w_associated_window *
450w_associate_find(struct w_associated_windowlist *aw, vaddr_t addr)
451{
452    int findResult = w_associate_find_index(aw, addr);
453    return findResult < 0 ? NULL : &aw->associated[findResult];
454}
455
456struct w_associated_window *
457w_associate_find_winID(struct w_associated_windowlist *aw, int winID)
458{
459    /* Re-sort the list if it's out of date. */
460    if (!aw->updated) {
461        w_associate_update(aw);
462        assert(aw->updated);
463    }
464
465    for (int i = 0; i < aw->numIndex; i++) {
466        if (aw->associated[i].winID == winID) {
467            return &aw->associated[i];
468        }
469    }
470    return NULL;
471}
472
473bool
474w_associate_check(struct w_associated_windowlist *aw, vaddr_t offset, vaddr_t size)
475{
476    /* Search for both the start vaddress and end vaddress. If either of them are valid,
477       then this window is invalid. Otherwise if the searched index are the same, then this
478       means there was not a window in between offset and size.
479    */
480    int findResult1 = w_associate_find_index(aw, offset);
481    if (findResult1 >= 0) {
482        return false;
483    }
484    int findResult2 = w_associate_find_index(aw, (offset + size - 1));
485    if (findResult2 >= 0) {
486        return false;
487    }
488    return (findResult1 == findResult2) ? true : false;
489}
490
491struct w_associated_window *
492w_associate_find_range(struct w_associated_windowlist *aw, vaddr_t offset, vaddr_t size)
493{
494    /* Search for both the start vaddress and end vaddress. If they both point to the same window,
495       then that window must contain the entire range. */
496    int findResult1 = w_associate_find_index(aw, offset);
497    if (findResult1 < 0) {
498        return NULL;
499    }
500    int findResult2 = w_associate_find_index(aw, (offset + size - 1));
501    if (findResult2 < 0) {
502        return NULL;
503    }
504    if (findResult1 != findResult2) {
505        /* A window ended and another window started in between our given range. This means
506           the given range is NOT contained by a single window. */
507        return NULL;
508    }
509    return &aw->associated[findResult1];
510}
511