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 "dataspace.h"
14#include "../../badge.h"
15#include "../../state.h"
16#include "../process/pid.h"
17#include <refos/refos.h>
18
19/*! @file
20    @brief Process Server anon RAM dataspace implementation. */
21
22extern seL4_MessageInfo_t _dispatcherEmptyReply;
23
24/* --------------------------- RAM dataspace OAT callback functions ----------------------------- */
25
26/*! @brief Dataspace OAT creation callback function.
27
28    This callback function is called by the OAT allocation helper library in <data_struct/coat.h>,
29    in order to create dataspace objects. Here we malloc some memory for the structure, initialise
30    its data structures, initialise its page array, and mint the dataspace badge capability.
31
32    @param oat The parent dataspace list (struct ram_dspace_list*).
33    @param id The dataspace ID allocated by the OAT table.
34    @param arg Arg[0] is the dataspace size, the rest unused.
35    @return A new dataspace (struct ram_dspace *) on success, NULL on error. (Transfers ownership)
36*/
37static cvector_item_t
38ram_dspace_oat_create(coat_t *oat, int id, uint32_t arg[COAT_ARGS])
39{
40    struct ram_dspace *ndspace = malloc(sizeof(struct ram_dspace));
41    if (!ndspace) {
42        ROS_ERROR("ram_dspace_oat_create out of memory!");
43        return NULL;
44    }
45    memset(ndspace, 0, sizeof(struct ram_dspace));
46    ndspace->magic = RAM_DATASPACE_MAGIC;
47    ndspace->ID = id;
48    ndspace->npages = (arg[0] / REFOS_PAGE_SIZE) + ((arg[0] % REFOS_PAGE_SIZE) ? 1 : 0);
49    ndspace->ref = 1;
50    ndspace->contentInitBitmask = NULL;
51    ndspace->contentInitEP.capPtr = 0;
52    ndspace->contentInitPID = PID_NULL;
53    ndspace->parentList = (struct ram_dspace_list *) oat;
54    assert(ndspace->parentList->magic == RAM_DATASPACE_LIST_MAGIC);
55
56    /* Initialise content init list. */
57    cvector_init(&ndspace->contentInitWaitingList);
58
59    /* Create the page array. */
60    ndspace->pages = kmalloc(sizeof(vka_object_t) * ndspace->npages);
61    if (!ndspace->pages) {
62        ROS_ERROR("ram_dspace_oat_create could not allocate page array, procserv out of mem!");
63        goto exit1;
64    }
65    memset(ndspace->pages, 0, sizeof(vka_object_t) * ndspace->npages);
66
67    /* Mint the badged capability representing this ram dataspace. */
68    ndspace->capability = procserv_mint_badge(RAM_DATASPACE_BADGE_BASE + id);
69    if (!ndspace->capability.capPtr) {
70        ROS_ERROR("ram_dspace_oat_create could not mint cap!");
71        goto exit2;
72    }
73
74    return (cvector_item_t) ndspace;
75
76    /* Exit stack. */
77exit2:
78    assert(ndspace->pages);
79    free(ndspace->pages);
80exit1:
81    free(ndspace);
82    return NULL;
83}
84
85/*! @brief Dataspace OAT deletion callback function.
86
87    This callback function is called by the OAT library defined in <data_struct/coat.h>, in order
88    to delete dataspace objects created by ram_dspace_oat_create(). It unmaps the dataspace from
89    all mapped windows, frees the caps, cslots, frames & frame arrays, and then the structure
90    itself.
91
92    @param oat The parent dataspace list (struct ram_dspace_list*).
93    @param obj The dataspace to delete (struct ram_dspace *) (Takes ownership).
94*/
95static void
96ram_dspace_oat_delete(coat_t *oat, cvector_item_t *obj)
97{
98    struct ram_dspace *rds = (struct ram_dspace *) obj;
99    assert(rds);
100    assert(rds->magic == RAM_DATASPACE_MAGIC);
101
102    /* Unmap everywhere where this dataspace has been mapped, by notifying the global procServ
103       window list that this dataspace has been deleted. This will loop through the windowList,
104       find any windows associated with this dspace, and unmap it and set back to EMPTY.
105       This potentially does VSpace mapping operations.
106    */
107    w_purge_dspace(&procServ.windowList, rds);
108
109    /* We now should be at ref 0. */
110    if (rds->ref != 0) {
111        ROS_WARNING("WARNING WARNING WARNING: RAM dataspace is being force released with hanging");
112        ROS_WARNING("strong reference. This is most likely a procserv bug. The most likely cause");
113        ROS_WARNING("is things such as a ring buffer object that has been attached to a");
114        ROS_WARNING("dataspace, that belongs to a RDS list which has been freed.");
115        assert(!"RAM dspace hanging reference. Process server bug.");
116    }
117
118    /* Free the content initialised bitmask. */
119    if (rds->contentInitBitmask) {
120        kfree(rds->contentInitBitmask);
121        rds->contentInitBitmask = NULL;
122    }
123
124    /* Free the content init endpoint & cslot. */
125    if (rds->contentInitEnabled) {
126        assert(rds->contentInitEP.capPtr);
127        vka_cnode_revoke(&rds->contentInitEP);
128        vka_cnode_delete(&rds->contentInitEP);
129        vka_cspace_free(&procServ.vka, rds->contentInitEP.capPtr);
130        rds->contentInitPID = PID_NULL;
131    }
132
133    /* Clear the content init waiting list. */
134    int waitingListCount = cvector_count(&rds->contentInitWaitingList);
135    for (int i = 0; i < waitingListCount; i++) {
136        struct ram_dspace_waiter *waiter = (struct ram_dspace_waiter *)
137                cvector_get(&rds->contentInitWaitingList, i);
138        assert(waiter && waiter->magic == RAM_DATASPACE_WAITER_MAGIC);
139        assert(waiter->reply.capPtr);
140
141        vka_cnode_revoke(&waiter->reply);
142        vka_cnode_delete(&waiter->reply);
143        vka_cspace_free(&procServ.vka, waiter->reply.capPtr);
144        kfree(waiter);
145    }
146    cvector_free(&rds->contentInitWaitingList);
147
148    /* Free the pages. */
149    assert(rds->pages);
150    for (int i = 0; i < rds->npages; i++) {
151        if (rds->pages[i].cptr) {
152            cspacepath_t path;
153            vka_cspace_make_path(&procServ.vka, rds->pages[i].cptr, &path);
154            vka_cnode_revoke(&path);
155            if (rds->physicalAddrEnabled) {
156                /* Frames belong to a device, we do not own this frame. Just delete the cslot. */
157                vka_cnode_delete(&path);
158                vka_cspace_free(&procServ.vka, path.capPtr);
159            } else {
160                /* We do own this anonymous dataspace frame. */
161                vka_free_object(&procServ.vka, &rds->pages[i]);
162            }
163        }
164    }
165    kfree(rds->pages);
166
167    /* Free the capability. */
168    assert(rds->capability.capPtr);
169    vka_cnode_revoke(&rds->capability);
170    vka_cnode_delete(&rds->capability);
171    vka_cspace_free(&procServ.vka, rds->capability.capPtr);
172
173    /* Free the actual window structure. */
174    free(rds);
175}
176
177/* ------------------------------- RAM dataspace table functions -------------------------------- */
178
179/*! @brief Calculates the page index into the dataspace based on the nbytes offset.
180    @param nbytes The offset into the ram dataspace.
181    @return The page index into the dataspace.
182 */
183static inline uint32_t
184ram_dspace_get_index(size_t nbytes)
185{
186    return (uint32_t)(nbytes / REFOS_PAGE_SIZE);
187}
188
189void
190ram_dspace_init(struct ram_dspace_list *rdslist)
191{
192    assert(rdslist);
193    dprintf("Initialising RAM dataspace allocation table (max %d dspaces).\n",
194            RAM_DATASPACE_MAX_NUM_DATASPACE);
195
196    /* Configure the object allocation table creation / deletion callback func pointers. */
197    rdslist->allocTable.oat_expand = NULL;
198    rdslist->allocTable.oat_create = ram_dspace_oat_create;
199    rdslist->allocTable.oat_delete = ram_dspace_oat_delete;
200    rdslist->magic = RAM_DATASPACE_LIST_MAGIC;
201
202    /* Initialise the allocation table. */
203    coat_init(&rdslist->allocTable, 1, RAM_DATASPACE_MAX_NUM_DATASPACE);
204}
205
206void
207ram_dspace_deinit(struct ram_dspace_list *rdslist)
208{
209    assert(rdslist);
210    for (int i = 1; i < RAM_DATASPACE_MAX_NUM_DATASPACE; i++) {
211        struct ram_dspace *dspace = ram_dspace_get(rdslist, i);
212        if (dspace) {
213            assert(dspace->magic == RAM_DATASPACE_MAGIC);
214            dspace->ref--;
215        }
216    }
217    coat_release(&rdslist->allocTable);
218}
219
220struct ram_dspace *
221ram_dspace_create(struct ram_dspace_list *rdslist, size_t size)
222{
223    assert(rdslist);
224    uint32_t arg[COAT_ARGS];
225    struct ram_dspace *dspace= NULL;
226
227    /* Allocate the dataspace ID and structure. */
228    arg[0] = (uint32_t) size;
229    int ID = coat_alloc(&rdslist->allocTable, arg, (cvector_item_t *) &dspace);
230    if (ID == RAM_DATASPACE_INVALID_ID) {
231        ROS_ERROR("Could not allocate window.");
232        return NULL;
233    }
234    assert(dspace && dspace->magic == RAM_DATASPACE_MAGIC);
235    return dspace;
236}
237
238void
239ram_dspace_ref(struct ram_dspace_list *rdslist, int ID)
240{
241    struct ram_dspace *dspace = ram_dspace_get(rdslist, ID);
242    if (!dspace) {
243        ROS_ERROR("ram_dspace_ref no such dataspace!");
244        assert(!"ram_dspace_ref no such dataspace! Procserv book keeping error.");
245        return;
246    }
247    assert(dspace->ref > 0);
248    dspace->ref++;
249}
250
251void
252ram_dspace_unref(struct ram_dspace_list *rdslist, int ID)
253{
254    struct ram_dspace *dspace = ram_dspace_get(rdslist, ID);
255    if (!dspace) {
256        ROS_ERROR("ram_dspace_unref no such dataspace!");
257        assert(!"ram_dspace_unref no such dataspace! Procserv book keeping error.");
258        return;
259    }
260    assert(dspace->ref > 0);
261    dspace->ref--;
262    if (dspace->ref == 0) {
263        /* Last reference, delete the object. */
264        coat_free(&rdslist->allocTable, ID);
265    }
266}
267
268seL4_CPtr
269ram_dspace_check_page(struct ram_dspace *dataspace, uint32_t offset)
270{
271    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
272    uint32_t idx = ram_dspace_get_index(offset);
273    if (idx >= dataspace->npages) {
274        /* Offset of of range. */
275        return (seL4_CPtr) 0;
276    }
277    return dataspace->pages[idx].cptr;
278}
279
280seL4_CPtr
281ram_dspace_get_page(struct ram_dspace *dataspace, uint32_t offset)
282{
283    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
284    uint32_t idx = ram_dspace_get_index(offset);
285    if (idx >= dataspace->npages) {
286        /* Offset of of range. */
287        return (seL4_CPtr) 0;
288    }
289    if (!dataspace->pages[idx].cptr) {
290        if (dataspace->physicalAddrEnabled) {
291            /* Allocate a physical address device memory region frame to fill this page. */
292            cspacepath_t deviceFrame = procserv_find_device(
293                    (void*) dataspace->physicalAddr + idx * REFOS_PAGE_SIZE, REFOS_PAGE_SIZE);
294            if (!deviceFrame.capPtr) {
295                ROS_WARNING("Could not allocate frame object. No such device.");
296                return (seL4_CPtr) 0;
297            }
298            memset(&dataspace->pages[idx], 0, sizeof(vka_object_t));
299            dataspace->pages[idx].cptr = deviceFrame.capPtr;
300        } else {
301            /* Allocate a normal frame to fill this page. */
302            int error = vka_alloc_frame(&procServ.vka, seL4_PageBits, &dataspace->pages[idx]);
303            if (error || !dataspace->pages[idx].cptr) {
304                ROS_ERROR("Could not allocate frame object. Procserv out of memory.");
305                return (seL4_CPtr) 0;
306            }
307        }
308    }
309    return dataspace->pages[idx].cptr;
310}
311
312struct ram_dspace *
313ram_dspace_get(struct ram_dspace_list *rdslist, int ID)
314{
315    if (ID <= RAM_DATASPACE_INVALID_ID || ID >= RAM_DATASPACE_MAX_NUM_DATASPACE) {
316        /* Invalid ID. */
317        return NULL;
318    }
319    struct ram_dspace* dspace = (struct ram_dspace*) coat_get(&rdslist->allocTable, ID);
320    if (!dspace) {
321        return NULL;
322    }
323    assert(dspace->magic == RAM_DATASPACE_MAGIC);
324    return dspace;
325}
326
327struct ram_dspace *
328ram_dspace_get_badge(struct ram_dspace_list *rdslist, seL4_Word badge)
329{
330    return ram_dspace_get(rdslist, badge - RAM_DATASPACE_BADGE_BASE);
331}
332
333uint32_t
334ram_dspace_get_size(struct ram_dspace *dataspace)
335{
336    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
337    return dataspace->npages * REFOS_PAGE_SIZE;
338}
339
340int
341ram_dspace_expand(struct ram_dspace *dataspace, uint32_t size)
342{
343    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
344    uint32_t npages = (size / REFOS_PAGE_SIZE) + ((size % REFOS_PAGE_SIZE) ? 1 : 0);
345
346    if (npages < dataspace->npages) {
347        /* Contraction not supported. */
348        return EINVALIDPARAM;
349    } else if (npages == dataspace->npages) {
350        /* Nothing to do here. */
351        return ESUCCESS;
352    }
353    uint32_t nbitmaskPrev = (dataspace->npages / 32) + 1;
354
355    /* Expand the dataspace. */
356    dataspace->pages = krealloc(dataspace->pages, sizeof(vka_object_t) * npages);
357    if (!dataspace->pages) {
358        ROS_ERROR("ram_dspace_expand could not reallocate page array, procserv out of mem!");
359        return ENOMEM; /* Easier to not clean up, leave extra bit of mem. */
360    }
361    uint32_t pageDiff = npages - dataspace->npages;
362    assert(pageDiff);
363    memset(&dataspace->pages[dataspace->npages], 0, sizeof(vka_object_t) * pageDiff);
364
365    /* Expand the dataspace content init mask. */
366    uint32_t nbitmask = (npages / 32) + 1;
367    if (dataspace->contentInitBitmask && nbitmaskPrev < nbitmask) {
368        dataspace->contentInitBitmask = krealloc (
369                dataspace->contentInitBitmask, nbitmask * sizeof(uint32_t)
370        );
371        if (!dataspace->contentInitBitmask) {
372            ROS_ERROR("ram_dspace_expand failed to realloc content init bitmask. Procserv OOM.");
373            return ENOMEM; /* Easier to not clean up, leave extra bit of mem. */
374        }
375        uint32_t bitmaskDiff = nbitmask - nbitmaskPrev;
376        assert(bitmaskDiff > 0);
377        memset(&dataspace->contentInitBitmask[nbitmaskPrev], 0, bitmaskDiff * sizeof(uint32_t));
378    }
379
380    dataspace->npages = npages;
381    return ESUCCESS;
382}
383
384int
385ram_dspace_set_to_paddr(struct ram_dspace *dataspace, uint32_t paddr)
386{
387    /* Check that the dataspace isn't already occupied with something. */
388    if (dataspace->contentInitEnabled) {
389        ROS_WARNING("Dataspace is already content init enabled, cannot set to physaddr mode.");
390        return EINVALID;
391    }
392    if (dataspace->physicalAddrEnabled) {
393        ROS_WARNING("Dataspace is already set to physaddr mode.");
394        return EINVALID;
395    }
396
397    /* Check that the dataspace is empty. */
398    dprintf("Checking pages...\n");
399    for (int i = 0; i < dataspace->npages; i++) {
400        if (dataspace->pages[i].cptr) {
401            ROS_WARNING("Dataspace already has mapped anonymous content.");
402            return EINVALID;
403        }
404    }
405
406    /* Quick sanity check that something actually exists at the given paddr. */
407    dprintf("procserv_find_device...\n");
408    cspacepath_t deviceFrame = procserv_find_device((void*) paddr, REFOS_PAGE_SIZE);
409    if (!deviceFrame.capPtr) {
410        ROS_WARNING("No such device 0x%x.", paddr);
411        return EFILENOTFOUND;
412    }
413    vka_cnode_delete(&deviceFrame);
414    vka_cspace_free(&procServ.vka, deviceFrame.capPtr);
415
416    dprintf("procserv_find_device OK...\n");
417    dataspace->physicalAddrEnabled = true;
418    dataspace->physicalAddr = paddr;
419    return ESUCCESS;
420}
421
422/* --------------------------- RAM dataspace read / write functions ----------------------------- */
423
424/*! @brief Reads data from a single page within a ram dataspace.
425
426    Reads data from a single page within a ram dataspace. Can _NOT_ read across page boundaries.
427    This is an internal helper function to implement the more general dataspace_read which can read
428    across page boundaries.
429    If the offset is not paged aligned, will only read data up to the next boundary. This should be
430    an internal function and not exposed, but is not declared static here to give special access to
431    unit tests.
432
433    @param buf The destination buffer to copy data to. (No ownership)
434    @param len The length of the data to be copied.
435    @param dataspace The source ram dataspace. (No ownership)
436    @param offset The offset into the dataspace to read from.
437    @return ESUCCESS if success, refos_err_t otherwise.
438 */
439int
440ram_dspace_read_page(char *buf, size_t len, struct ram_dspace *dataspace, uint32_t offset)
441{
442    vaddr_t skipBytes = offset - REFOS_PAGE_ALIGN(offset);
443    if (len > (REFOS_PAGE_SIZE - skipBytes)) {
444        dvprintf("WARNING: capping at len > PAGE_SIZE - skipBytes.\n");
445        len = (REFOS_PAGE_SIZE - skipBytes);
446    }
447    seL4_CPtr frame = ram_dspace_get_page(dataspace, offset);
448    if (!frame) {
449        ROS_ERROR("ram_dspace_read_page failed to allocate page. Procserv out of memory.");
450        return ENOMEM;
451    }
452    return procserv_frame_read(frame, buf, len, skipBytes);
453}
454
455/*! @brief Writes data to a page within a ram dataspace.
456
457    Writes data to a page within a ram dataspace. Can _NOT_ write across page boundaries.
458    This is an internal helper function to implement the more general dataspace_write which can write
459    across page boundaries.
460    If the offset is not paged aligned, will only write data up to the next boundary. This should be
461    an internal function and not exposed, but is not declared static here to give special access to
462    unit tests.
463
464    @param buf The source buffer containing the data. (No ownership)
465    @param len The length of the data to be written.
466    @param dataspace The destination dataspace to be written to. (No ownership)
467    @param offset The offset into the dataspace to write to.
468    @return ESUCCESS if success, refos_err_t otherwise.
469 */
470int
471ram_dspace_write_page(char *buf, size_t len, struct ram_dspace *dataspace, uint32_t offset)
472{
473    vaddr_t skipBytes = offset - REFOS_PAGE_ALIGN(offset);
474    if (len > (REFOS_PAGE_SIZE - skipBytes)) {
475        dvprintf("WARNING: capping at len > PAGE_SIZE - skipBytes.\n");
476        len = (REFOS_PAGE_SIZE - skipBytes);
477    }
478    seL4_CPtr frame = ram_dspace_get_page(dataspace, offset);
479    if (!frame) {
480        ROS_ERROR("ram_dataspace_write_page failed to allocate page. Procserv out of memory.");
481        return ENOMEM;
482    }
483    return procserv_frame_write(frame, buf, len, skipBytes);
484}
485
486int
487ram_dspace_read(char *buf, size_t len, struct ram_dspace *dataspace, uint32_t offset)
488{
489    assert(buf && dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
490    int start = offset;
491    int bufOffset = 0;
492
493    /* Check if the read length runs off the end of the dataspace. */
494    if (len > ((dataspace->npages * REFOS_PAGE_SIZE) - offset)) {
495        return EINVALIDPARAM;
496    }
497
498    while (start < offset + len) {
499        int end = MIN(REFOS_PAGE_ALIGN(start) + REFOS_PAGE_SIZE, offset + len);
500
501        /* Read data out of the next page. */
502        int error = ram_dspace_read_page (
503                (char*)(buf + bufOffset), end - start,
504                dataspace, start
505        );
506        if (error) {
507            ROS_ERROR("ram_dspace_read_page failed.");
508            return error;
509        }
510
511        bufOffset += end - start;
512        start = end;
513    }
514    return ESUCCESS;
515}
516
517int
518ram_dspace_write(char *buf, size_t len, struct ram_dspace *dataspace, uint32_t offset)
519{
520    assert(buf && dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
521    int start = offset;
522    int bufOffset = 0;
523
524    /* Check if the write length runs off the end of the dataspace. */
525    if (len > ((dataspace->npages * REFOS_PAGE_SIZE) - offset)) {
526        return EINVALIDPARAM;
527    }
528
529    while (start < offset + len) {
530        int end = MIN(REFOS_PAGE_ALIGN(start) + REFOS_PAGE_SIZE, offset + len);
531
532        /* Write data into the next page. */
533        int error = ram_dspace_write_page (
534                (char*)(buf + bufOffset), end - start,
535                dataspace, start
536        );
537        if (error) {
538            ROS_ERROR("ram_dspace_write_page failed.");
539            return error;
540        }
541
542        bufOffset += end - start;
543        start = end;
544    }
545
546    return ESUCCESS;
547}
548
549/* --------------------------- RAM dataspace content init functions ----------------------------- */
550
551int
552ram_dspace_content_init(struct ram_dspace *dataspace, cspacepath_t initEP, uint32_t initPID)
553{
554    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
555
556    /* We can't content init a dataspace that is physical address bound. */
557    if (dataspace->physicalAddrEnabled) {
558        ROS_WARNING("Can't content init a dataspace that is physical address bound.");
559        return EINVALID;
560    }
561
562    /* Free any previous content initialised bitmasks. */
563    if (dataspace->contentInitBitmask) {
564        kfree(dataspace->contentInitBitmask);
565        dataspace->contentInitBitmask = NULL;
566    }
567
568    /* Free any previous content initialisation endpoints. */
569    if (dataspace->contentInitEnabled) {
570        assert(dataspace->contentInitEP.capPtr);
571        vka_cnode_revoke(&dataspace->contentInitEP);
572        vka_cnode_delete(&dataspace->contentInitEP);
573        vka_cspace_free(&procServ.vka, dataspace->contentInitEP.capPtr);
574        dataspace->contentInitEP.capPtr = 0;
575    }
576
577    /* Special case, no endpoint, means unset content initialisation. */
578    if (initEP.capPtr == 0) {
579        dataspace->contentInitEnabled = false;
580        dataspace->contentInitPID = PID_NULL;
581        return ESUCCESS;
582    }
583
584    /* Allocate bitmask. */
585    uint32_t nbitmask = (dataspace->npages / 32) + 1;
586    dataspace->contentInitBitmask = kmalloc(nbitmask * sizeof(uint32_t));
587    if (!dataspace->contentInitBitmask) {
588        ROS_ERROR("ram_dspace_content_init failed to malloc content init bitmask. Procserv OOM.");
589        return ENOMEM;
590    }
591    memset(dataspace->contentInitBitmask, 0, nbitmask * sizeof(uint32_t));
592
593    /* Clear the waiting list. */
594    int waitingListCount = cvector_count(&dataspace->contentInitWaitingList);
595    for (int i = 0; i < waitingListCount; i++) {
596        struct ram_dspace_waiter *waiter = (struct ram_dspace_waiter *)
597                cvector_get(&dataspace->contentInitWaitingList, i);
598        assert(waiter && waiter->magic == RAM_DATASPACE_WAITER_MAGIC);
599        assert(waiter->reply.capPtr);
600
601        vka_cnode_revoke(&waiter->reply);
602        vka_cnode_delete(&waiter->reply);
603        vka_cspace_free(&procServ.vka, waiter->reply.capPtr);
604        kfree(waiter);
605    }
606    cvector_free(&dataspace->contentInitWaitingList);
607    cvector_init(&dataspace->contentInitWaitingList);
608
609    /* Set the content EP, taking ownership of given endpoint. */
610    dataspace->contentInitEP = initEP;
611    dataspace->contentInitEnabled = true;
612    dataspace->contentInitPID = initPID;
613    return ESUCCESS;
614}
615
616int
617ram_dspace_need_content_init(struct ram_dspace *dataspace, uint32_t offset)
618{
619    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
620
621    if (!dataspace->contentInitEnabled || !dataspace->contentInitBitmask) {
622        return -EINVALID;
623    }
624    if (offset > ram_dspace_get_size(dataspace)) {
625        return -EINVALIDPARAM;
626    }
627
628    uint32_t npage = (offset / REFOS_PAGE_SIZE);
629    uint32_t idxbitmask = npage / 32;
630    uint32_t idxshift = npage % 32;
631    assert(npage <= dataspace->npages);
632
633    return !((dataspace->contentInitBitmask[idxbitmask] >> idxshift) & 0x1);
634}
635
636int
637ram_dspace_add_content_init_waiter(struct ram_dspace *dataspace, uint32_t offset,
638                                   cspacepath_t reply)
639{
640    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
641    if (!reply.capPtr) {
642        ROS_WARNING("add_content_init_waiter: null cap given. Nothing to do.");
643        return EINVALIDPARAM;
644    }
645    if (offset > ram_dspace_get_size(dataspace)) {
646        return EINVALIDPARAM;
647    }
648    if (!dataspace->contentInitEnabled) {
649        dvprintf("add_content_init_waiter: content init not enabled.");
650        return EINVALID;
651    }
652    uint32_t npage = (offset / REFOS_PAGE_SIZE);
653    assert(npage < dataspace->npages);
654
655    /* Allocate the waiter structure. */
656    struct ram_dspace_waiter* waiter = kmalloc(sizeof(struct ram_dspace_waiter));
657    if (!waiter) {
658        ROS_ERROR("add_content_init_waiter could not malloc waiter struct. Procserv OOM.");
659        return ENOMEM;
660    }
661
662    /* Fill out the waiter structure and add to waiting list. */
663    waiter->magic = RAM_DATASPACE_WAITER_MAGIC;
664    waiter->pageidx = npage;
665    waiter->reply = reply;
666    cvector_add(&dataspace->contentInitWaitingList, (cvector_item_t) waiter);
667    return ESUCCESS;
668}
669
670int
671ram_dspace_add_content_init_waiter_save_current_caller(struct ram_dspace *dataspace,
672                                                       uint32_t offset)
673{
674    /* Allocate anew cslot to save the reply cap to. */
675    cspacepath_t callerReply;
676    int error = vka_cspace_alloc_path(&procServ.vka, &callerReply);
677    if (error != seL4_NoError) {
678        ROS_ERROR("Could not allocate path to save caller. PRocserv outof cslots.");
679        return ENOMEM;
680    }
681    assert(callerReply.capPtr);
682
683    /* Save the caller cap. */
684    error = vka_cnode_saveCaller(&callerReply);
685    if (error != seL4_NoError) {
686        ROS_ERROR("Could not copy out reply cap.");
687        return EINVALID;
688    }
689
690    /* Add reply cap as waiter (takes ownership of the cap) */
691    return ram_dspace_add_content_init_waiter(dataspace, offset, callerReply);
692}
693
694
695void
696ram_dspace_content_init_reply_waiters(struct ram_dspace *dataspace, uint32_t offset)
697{
698    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
699
700    /* Calculate (and check) the dspace page number. */
701    uint32_t npage = (offset / REFOS_PAGE_SIZE);
702    if(npage >= dataspace->npages) {
703        ROS_ERROR("ram_dspace_content_init_reply_waiters: offset out of bounds.")
704        return;
705    }
706
707    /* Loop through the waiting list, find any clients that are currently blocked on the page
708       we have data for, and reply to them. */
709    int waitingListCount = cvector_count(&dataspace->contentInitWaitingList);
710    for (int i = 0; i < waitingListCount; i++) {
711        struct ram_dspace_waiter *waiter = (struct ram_dspace_waiter *)
712                cvector_get(&dataspace->contentInitWaitingList, i);
713        assert(waiter && waiter->magic == RAM_DATASPACE_WAITER_MAGIC);
714        assert(waiter->reply.capPtr);
715
716        if (waiter->pageidx == npage) {
717            /* Unblock this client. */
718            seL4_Send(waiter->reply.capPtr, _dispatcherEmptyReply);
719
720            /* Remove this waiter from the waiting list. */
721            cvector_delete(&dataspace->contentInitWaitingList, i);
722            waitingListCount--;
723            assert(waitingListCount == cvector_count(&dataspace->contentInitWaitingList));
724            i--;
725
726            /* Delete this waiter. */
727            vka_cnode_revoke(&waiter->reply);
728            vka_cnode_delete(&waiter->reply);
729            vka_cspace_free(&procServ.vka, waiter->reply.capPtr);
730            kfree(waiter);
731        }
732    }
733}
734
735void
736ram_dspace_set_content_init_provided(struct ram_dspace *dataspace, uint32_t offset)
737{
738    assert(dataspace && dataspace->magic == RAM_DATASPACE_MAGIC);
739
740    if (!dataspace->contentInitEnabled || !dataspace->contentInitBitmask) {
741        ROS_WARNING("set_content_init_provided called with content init disabled.");
742        return;
743    }
744    if (offset > ram_dspace_get_size(dataspace)) {
745        ROS_WARNING("set_content_init_provided offset out-of-bounds.");
746        return;
747    }
748
749    uint32_t npage = (offset / REFOS_PAGE_SIZE);
750    uint32_t idxbitmask = npage / 32;
751    uint32_t idxshift = npage % 32;
752    assert(npage <= dataspace->npages);
753
754    /* Set the bitmask bit. */
755    dataspace->contentInitBitmask[idxbitmask] |= (1 << idxshift);
756}
757
758
759