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 <assert.h> 14#include <refos/vmlayout.h> 15#include <refos/error.h> 16#include <refos-io/mmap_segment.h> 17#include <refos-io/internal_state.h> 18#include <refos-util/dprintf.h> 19#include <refos-util/init.h> 20#include <refos-rpc/proc_client.h> 21#include <refos-rpc/proc_client_helper.h> 22 23#define REFOS_IO_INTERNAL_MMAP_PAGE_STATUS_BUFFER_SIZE 0x11000 24static char _refosioMMapPageStatusBuffer[REFOS_IO_INTERNAL_MMAP_PAGE_STATUS_BUFFER_SIZE]; 25 26#define REFOS_IO_INTERNAL_MMAP_SEGMENT_BUFFER_SIZE 0x1000 27static char _refosioMMapSegmentStatusBuffer[REFOS_IO_INTERNAL_MMAP_SEGMENT_BUFFER_SIZE]; 28 29void 30refosio_mmap_init(refos_io_mmap_segment_state_t *s) 31{ 32 cbpool_init_static(&s->mmapRegionPageStatus, PROCESS_MMAP_LIMIT_SIZE_NPAGES, 33 _refosioMMapPageStatusBuffer, REFOS_IO_INTERNAL_MMAP_PAGE_STATUS_BUFFER_SIZE); 34 cbpool_init_static(&s->mmapRegionSegmentStatus, PROCESS_MMAP_SEGMENTS, 35 _refosioMMapSegmentStatusBuffer, REFOS_IO_INTERNAL_MMAP_SEGMENT_BUFFER_SIZE); 36} 37 38int 39refosio_mmap_segment_fill(refos_io_mmap_segment_state_t *s, uint32_t vaddrOffsetPage) 40{ 41 int error = EINVALID; 42 assert(vaddrOffsetPage >= PROCESS_MMAP_BOT && vaddrOffsetPage < PROCESS_MMAP_TOP); 43 uint32_t segmentID = (PROCESS_MMAP_TOP - vaddrOffsetPage) / 44 (PROCESS_MMAP_SEGMENT_SIZE_NPAGES * REFOS_PAGE_SIZE); 45 assert(segmentID < PROCESS_MMAP_SEGMENTS); 46 47 if (cbpool_check_single(&s->mmapRegionSegmentStatus, segmentID)) { 48 /* This segment is already filled. Nothing to do here. */ 49 return ESUCCESS; 50 } 51 52 /* Create the window. */ 53 uint32_t vaddr = PROCESS_MMAP_TOP - 54 ((segmentID + 1) * (PROCESS_MMAP_SEGMENT_SIZE_NPAGES * REFOS_PAGE_SIZE)); 55 assert(vaddr >= PROCESS_MMAP_BOT && vaddr < PROCESS_MMAP_TOP); 56 57 seL4_CPtr window = proc_create_mem_window(vaddr, 58 PROCESS_MMAP_SEGMENT_SIZE_NPAGES * REFOS_PAGE_SIZE); 59 if (!window || REFOS_GET_ERRNO() != ESUCCESS) { 60 seL4_DebugPrintf("mmap_segment_fill: Could not create window.\n"); 61 return EINVALIDWINDOW; 62 } 63 64 /* Create the dataspace. */ 65 seL4_CPtr dataspace = data_open(REFOS_PROCSERV_EP, "anon", 0, 0, 66 PROCESS_MMAP_SEGMENT_SIZE_NPAGES * REFOS_PAGE_SIZE, &error); 67 if (error) { 68 seL4_DebugPrintf("mmap_segment_fill: Could not create anon dspace.\n"); 69 error = ENOMEM; 70 goto exit1; 71 } 72 73 /* Map the segment dataspace into the window. */ 74 error = data_datamap(REFOS_PROCSERV_EP, dataspace, window, 0); 75 if (error != ESUCCESS) { 76 seL4_DebugPrintf("mmap_segment_fill: Could not map anon dspace.\n"); 77 goto exit2; 78 } 79 80 /* Set the segment allocated status to TRUE. */ 81 cbpool_set_single(&s->mmapRegionSegmentStatus, segmentID, true); 82 83 csfree_delete(dataspace); 84 csfree_delete(window); 85 return ESUCCESS; 86 87 /* Exit stack. */ 88exit2: 89 data_close(REFOS_PROCSERV_EP, dataspace); 90 csfree_delete(dataspace); 91exit1: 92 proc_delete_mem_window(window); 93 csfree_delete(window); 94 return error; 95} 96 97int 98refosio_mmap_segment_release(refos_io_mmap_segment_state_t *s, uint32_t vaddrOffsetPage) 99{ 100 assert(vaddrOffsetPage >= PROCESS_MMAP_BOT && vaddrOffsetPage < PROCESS_MMAP_TOP); 101 uint32_t segmentID = (PROCESS_MMAP_TOP - vaddrOffsetPage) / 102 (PROCESS_MMAP_SEGMENT_SIZE_NPAGES * REFOS_PAGE_SIZE); 103 assert(segmentID < PROCESS_MMAP_SEGMENTS); 104 105 if (!cbpool_check_single(&s->mmapRegionSegmentStatus, segmentID)) { 106 /* This segment is already released. Nothing to do here. */ 107 return ESUCCESS; 108 } 109 110 /* Check that every page associated has neem release. Otherwise we don't unmap yet. */ 111 for (int i = 0; i < PROCESS_MMAP_SEGMENT_SIZE_NPAGES; i++) { 112 uint32_t page = ( (PROCESS_MMAP_TOP - vaddrOffsetPage) / REFOS_PAGE_SIZE ) + i; 113 if (cbpool_check_single(&s->mmapRegionPageStatus, page)) { 114 /* A page is still mapped here. */ 115 return EUNMAPFIRST; 116 } 117 } 118 119 /* Get the window. */ 120 seL4_CPtr window = proc_get_mem_window(vaddrOffsetPage); 121 if (!window) { 122 /* Nothing mapped here. Nothing to do. */ 123 seL4_DebugPrintf("mmap_segment_release: No window to release. Doing nothing.\n"); 124 return ESUCCESS; 125 } 126 127 /* Get the associated dataspace and release it. */ 128 refos_err_t refos_err = -EINVALID; 129 seL4_CPtr dspace = proc_get_mem_window_dspace(window, &refos_err); 130 if (refos_err != ESUCCESS) { 131 seL4_DebugPrintf("mmap_segment_fill: Failed to get window dataspace, error occued.\n"); 132 return (int) refos_err; 133 } 134 if (dspace) { 135 int error = data_close(REFOS_PROCSERV_EP, dspace); 136 if (error) { 137 seL4_DebugPrintf("mmap_segment_fill: Failed delete dataspace.\n"); 138 } 139 csfree_delete(dspace); 140 } 141 142 /* Finally, delete the window. */ 143 int error = proc_delete_mem_window(window); 144 if (error) { 145 seL4_DebugPrintf("mmap_segment_fill: Failed to delete window, error occued.\n"); 146 } 147 csfree_delete(window); 148 149 /* Set the segment allocated status back to FALSE. */ 150 cbpool_set_single(&s->mmapRegionSegmentStatus, segmentID, false); 151 return ESUCCESS; 152} 153 154int 155refosio_mmap_anon(refos_io_mmap_segment_state_t *s, int npages, uint32_t *vaddrDest) 156{ 157 if (!npages) { 158 /* Nothing to do here. */ 159 return ESUCCESS; 160 } 161 assert(s); 162 163 /* Allocate the mmap pages from the page bitmap allocator. */ 164 uint32_t vaddrOffsetPage = cbpool_alloc(&s->mmapRegionPageStatus, npages); 165 if (vaddrOffsetPage == CBPOOL_INVALID) { 166 seL4_DebugPrintf("mmap_anon: Could not allocate page region. Out of virtual memory.\n"); 167 return ENOMEM; 168 } 169 170 /* Loop through every segment and fill it in. */ 171 for (int i = 0; i < npages; i++) { 172 uint32_t vaddr = PROCESS_MMAP_TOP - ((vaddrOffsetPage + i + 1) * REFOS_PAGE_SIZE); 173 174 /* Fill in the segment. If the segment already is filled then it does nothing. */ 175 int error = refosio_mmap_segment_fill(s, vaddr); 176 if (error != ESUCCESS) { 177 seL4_DebugPrintf("mmap_segment_fill failed. Cannot fully recover from this.\n"); 178 179 /* All the segments up to this point have been allocated. All the segments here and 180 after have not been, so unset them. This prevents inconsistent state between the page 181 bitmap and actually mapped pages. */ 182 183 cbpool_free(&s->mmapRegionPageStatus, vaddrOffsetPage + i, npages - i); 184 return error; 185 } 186 } 187 188 if (vaddrDest) { 189 (*vaddrDest) = PROCESS_MMAP_TOP - ((vaddrOffsetPage + npages) * REFOS_PAGE_SIZE); 190 } 191 return ESUCCESS; 192} 193 194int 195refosio_munmap_anon(refos_io_mmap_segment_state_t *s, uint32_t vaddr, int npages) 196{ 197 if (npages == 0) { 198 /* Nothing to do here. */ 199 return ESUCCESS; 200 } 201 if (vaddr >= PROCESS_MMAP_TOP) { 202 seL4_DebugPrintf("unmap_anon: invalid vaddr, too high."); 203 return EINVALIDPARAM; 204 } 205 if (vaddr < PROCESS_MMAP_BOT) { 206 seL4_DebugPrintf("unmap_anon: invalid vaddr, too low."); 207 return EINVALIDPARAM; 208 } 209 210 /* Free every page in the range. */ 211 uint32_t vaddrOffsetPage = (PROCESS_MMAP_TOP - vaddr) / REFOS_PAGE_SIZE; 212 assert(vaddrOffsetPage < PROCESS_MMAP_LIMIT_SIZE_NPAGES); 213 cbpool_free(&s->mmapRegionPageStatus, vaddrOffsetPage, npages); 214 215 /* Loop through every segment and check for affected. */ 216 for (int i = 0; i < npages; i++) { 217 uint32_t vaddr = PROCESS_MMAP_TOP - ((vaddrOffsetPage + i + 1) * REFOS_PAGE_SIZE); 218 int error = refosio_mmap_segment_release(s, vaddr); 219 if (error != ESUCCESS && error != EUNMAPFIRST) { 220 /* Best and easiest thing we can do here is just leak memory. */ 221 seL4_DebugPrintf("refosio_mmap_segment_release failed. Leaked memory.\n"); 222 } 223 } 224 225 return ESUCCESS; 226}