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/*! \file mem_syscall.c 14 @brief Dispatcher for the procserv memory related syscalls. 15*/ 16 17#include "proc_syscall.h" 18#include "mem_syscall.h" 19 20#include <refos/vmlayout.h> 21#include <refos-rpc/proc_server.h> 22 23#include "../system/process/process.h" 24#include "../system/memserv/window.h" 25#include "../system/addrspace/vspace.h" 26 27/*! @file 28 @brief Handles process server memory-related syscalls. 29 30 This module handles memory-related process server syscalls, functions defined in the generated 31 file <refos-rpc/proc_server.h>. Although technically these syscalls are still process server 32 interface syscalls and should belong in proc_syscall module, there is enough code here to be 33 separted into two files for better code organisation. 34*/ 35 36/*! @brief Handles memory window creation syscalls. 37 38 The window must not be overlapping with an existing window in the client's VSpace, or 39 EINVALIDPARAM will be the returned. 40 41 When mapping a dataspace to a non-page aligned window, the dataspace will actually be mapped to 42 the page-aligned address of the window base due to technical restrictions. Thus, the first B - 43 PAGE_ALIGN(B) bytes of the mapped dataspace is unaccessible. This can have unintended effects 44 when two processes map the same dataspace for sharing purposes. In other words, when sharing 45 dataspaces, it's easiest for the window bases for BOTH processes to be page-aligned. 46 */ 47seL4_CPtr 48proc_create_mem_window_internal_handler(void *rpc_userptr , uint32_t rpc_vaddr , uint32_t rpc_size , 49 uint32_t rpc_permissions, uint32_t flags, 50 refos_err_t* rpc_errno) 51{ 52 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 53 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 54 55 /* Check that this window does not override protected kernel memory. */ 56 if (rpc_vaddr >= PROCESS_KERNEL_RESERVED || PROCESS_KERNEL_RESERVED < (rpc_size + rpc_vaddr)) { 57 dvprintf("memory window out of bounds, overlaps kernel reserved.\n"); 58 SET_ERRNO_PTR(rpc_errno, EINVALIDPARAM); 59 return 0; 60 } 61 62 /* Create the window. */ 63 int windowID = W_INVALID_WINID; 64 bool cached = (flags & W_FLAGS_UNCACHED) ? false : true; 65 int error = vs_create_window(&pcb->vspace, rpc_vaddr, rpc_size, rpc_permissions, cached, 66 &windowID); 67 if (error != ESUCCESS || windowID == W_INVALID_WINID) { 68 dvprintf("Could not create window.\n"); 69 SET_ERRNO_PTR(rpc_errno, error); 70 return 0; 71 } 72 73 /* Find the window and return the window capability. */ 74 struct w_window* window = w_get_window(&procServ.windowList, windowID); 75 if (!window) { 76 assert(!"Successfully allocated window failed to be found. Process server bug."); 77 /* Cannot recover from this situation cleanly. Shouldn't ever happen. */ 78 SET_ERRNO_PTR(rpc_errno, EINVALID); 79 return 0; 80 } 81 82 assert(window->magic == W_MAGIC); 83 assert(window->capability.capPtr); 84 SET_ERRNO_PTR(rpc_errno, ESUCCESS); 85 return window->capability.capPtr; 86} 87 88/*! @brief Handles memory window resize syscalls. */ 89refos_err_t 90proc_resize_mem_window_handler(void *rpc_userptr , seL4_CPtr rpc_window , uint32_t rpc_size) 91{ 92 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 93 struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr; 94 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 95 96 if (!check_dispatch_caps(m, 0x00000001, 1)) { 97 dvprintf("Warning: proc_resize_mem_window invalid window cap.\n"); 98 return EINVALIDWINDOW; 99 } 100 101 if (!dispatcher_badge_window(rpc_window)) { 102 dvprintf("Warning: proc_resize_mem_window invalid window badge.\n"); 103 return EINVALIDWINDOW; 104 } 105 106 /* Perform the actual window resize operation. */ 107 return vs_resize_window(&pcb->vspace, rpc_window - W_BADGE_BASE, rpc_size); 108} 109 110/*! @brief Handles memory window deletion syscalls. */ 111refos_err_t 112proc_delete_mem_window_handler(void *rpc_userptr , seL4_CPtr rpc_window) 113{ 114 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 115 struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr; 116 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 117 118 if (!check_dispatch_caps(m, 0x00000001, 1)) { 119 return EINVALIDWINDOW; 120 } 121 122 if (!dispatcher_badge_window(rpc_window)) { 123 return EINVALIDWINDOW; 124 } 125 126 /* Perform the actual window deletion. Also unmaps the window. */ 127 vs_delete_window(&pcb->vspace, rpc_window - W_BADGE_BASE); 128 return ESUCCESS; 129} 130 131seL4_CPtr 132proc_get_mem_window_handler(void *rpc_userptr , uint32_t rpc_vaddr) 133{ 134 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 135 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 136 137 /* Find the window at the vaddr. */ 138 struct w_associated_window *aw = w_associate_find(&pcb->vspace.windows, rpc_vaddr); 139 if (!aw) { 140 return 0; 141 } 142 143 /* Retrieve the window from global window list. */ 144 struct w_window *window = w_get_window(&procServ.windowList, aw->winID); 145 if (!window) { 146 ROS_ERROR("Failed to find associated window in global list. Procserv book-keeping bug."); 147 return 0; 148 } 149 150 assert(window->capability.capPtr); 151 return window->capability.capPtr; 152} 153 154seL4_CPtr 155proc_get_mem_window_dspace_handler(void *rpc_userptr , seL4_CPtr rpc_window , 156 refos_err_t* rpc_errno) 157{ 158 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 159 struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr; 160 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 161 162 if (!check_dispatch_caps(m, 0x00000001, 1)) { 163 SET_ERRNO_PTR(rpc_errno, EINVALIDWINDOW); 164 return 0; 165 } 166 167 if (!dispatcher_badge_window(rpc_window)) { 168 SET_ERRNO_PTR(rpc_errno, EINVALIDWINDOW); 169 return 0; 170 } 171 172 /* Retrieve the window from global window list. */ 173 struct w_window *window = w_get_window(&procServ.windowList, rpc_window - W_BADGE_BASE); 174 if (!window) { 175 ROS_ERROR("Failed to find associated window in global list. Procserv book-keeping bug."); 176 SET_ERRNO_PTR(rpc_errno, EINVALIDWINDOW); 177 return 0; 178 } 179 180 if (window->mode != W_MODE_ANONYMOUS) { 181 SET_ERRNO_PTR(rpc_errno, ESUCCESS); 182 return 0; 183 } 184 assert(window->ramDataspace && window->ramDataspace->magic == RAM_DATASPACE_MAGIC); 185 186 SET_ERRNO_PTR(rpc_errno, ESUCCESS); 187 return window->ramDataspace->capability.capPtr; 188} 189 190 191/*! @brief Handles server pager setup syscalls. 192 193 A dataserver calls the process server with this call in order to set up to be the pager of 194 one of its client processes for a particular window. The client process is identified by the 195 passing of its liveliness cap. All faults for the client's process which happen at that window 196 will then be delegated to the dataserver to be handled. 197*/ 198refos_err_t 199proc_register_as_pager_handler(void *rpc_userptr , seL4_CPtr rpc_window , 200 seL4_CPtr rpc_faultNotifyEP , seL4_Word* rpc_winID) 201{ 202 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 203 struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr; 204 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 205 206 if (!check_dispatch_caps(m, 0x00000001, 2)) { 207 return EINVALIDPARAM; 208 } 209 210 /* Retrieve and verify the window cap. */ 211 if (!dispatcher_badge_window(rpc_window)) { 212 ROS_WARNING("Invalid window badge."); 213 return EINVALIDPARAM; 214 } 215 struct w_window *win = w_get_window(&procServ.windowList, rpc_window - W_BADGE_BASE); 216 if (!win) { 217 ROS_ERROR("invalid window ID."); 218 return EINVALIDPARAM; 219 } 220 assert(win->magic == W_MAGIC); 221 222 /* Copy out the fault endpoint. */ 223 seL4_CPtr faultNotifyEP = dispatcher_copyout_cptr(rpc_faultNotifyEP); 224 if (!faultNotifyEP) { 225 dvprintf("could not copy out faultNotifyEP."); 226 return EINVALIDPARAM; 227 } 228 229 /* Set the pager endpoint. If there was anything else mapped to this window, it will be 230 unmapped. */ 231 cspacepath_t faultNotifyEPPath; 232 vka_cspace_make_path(&procServ.vka, faultNotifyEP, &faultNotifyEPPath); 233 w_set_pager_endpoint(win, faultNotifyEPPath, pcb->pid); 234 235 if (rpc_winID) { 236 (*rpc_winID) = win->wID; 237 } 238 return ESUCCESS; 239} 240 241refos_err_t 242proc_unregister_as_pager_handler(void *rpc_userptr , seL4_CPtr rpc_window) 243{ 244 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 245 struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr; 246 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 247 248 if (!check_dispatch_caps(m, 0x00000001, 1)) { 249 return EINVALIDPARAM; 250 } 251 252 /* Retrieve and verify the window cap. */ 253 if (!dispatcher_badge_window(rpc_window)) { 254 ROS_WARNING("Invalid window badge."); 255 return EINVALIDPARAM; 256 } 257 struct w_window *win = w_get_window(&procServ.windowList, rpc_window - W_BADGE_BASE); 258 if (!win) { 259 ROS_ERROR("invalid window ID."); 260 return EINVALIDPARAM; 261 } 262 assert(win->magic == W_MAGIC); 263 264 /* Unset the pager endpoint. If there was anything else mapped to this window, it will be 265 unmapped. */ 266 cspacepath_t emptyPath; 267 memset(&emptyPath, 0, sizeof(cspacepath_t)); 268 w_set_pager_endpoint(win, emptyPath, PID_NULL); 269 return ESUCCESS; 270} 271 272/*! @brief Handles server notification buffer setup syscalls. 273 274 A server calls this on the process server in order to set up its notification buffer, used 275 for notification messages such as content-initialisation and pager fault-delegation. 276 */ 277refos_err_t 278proc_notification_buffer_handler(void *rpc_userptr , seL4_CPtr rpc_dataspace) 279{ 280 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 281 struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr; 282 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 283 284 if (!check_dispatch_caps(m, 0x00000001, 1)) { 285 return EINVALIDPARAM; 286 } 287 288 /* Verify & find the dataspace. */ 289 if (!dispatcher_badge_dspace(rpc_dataspace)) { 290 return EINVALIDPARAM; 291 } 292 struct ram_dspace *dspace = ram_dspace_get_badge(&procServ.dspaceList, rpc_dataspace); 293 if (!dspace) { 294 return EINVALIDPARAM; 295 } 296 297 /* Set the notification buffer. */ 298 return proc_set_notificationbuffer(pcb, dspace); 299} 300 301/*! @brief Handles server window map syscalls. 302 303 A server calls this on the process server in response to a prior fault delegation notification 304 made by the process server, in order to map the given frame in the dataserver's VSpace into 305 the faulting address frame, resolving the fault. 306 */ 307refos_err_t 308proc_window_map_handler(void *rpc_userptr , seL4_CPtr rpc_window , uint32_t rpc_windowOffset , 309 uint32_t rpc_srcAddr) 310{ 311 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 312 struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr; 313 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 314 315 if (!check_dispatch_caps(m, 0x00000001, 1)) { 316 return EINVALIDPARAM; 317 } 318 319 /* Retrieve and verify the window cap. */ 320 if (!dispatcher_badge_window(rpc_window)) { 321 return EINVALIDPARAM; 322 } 323 struct w_window *window = w_get_window(&procServ.windowList, rpc_window - W_BADGE_BASE); 324 if (!window) { 325 ROS_ERROR("window does not exist!\n"); 326 return EINVALIDWINDOW; 327 } 328 329 /* Map the frame from src vspace to dest vspace. */ 330 struct proc_pcb *clientPCB = NULL; 331 int error = vs_map_across_vspace(&pcb->vspace, rpc_srcAddr, window, rpc_windowOffset, 332 &clientPCB); 333 if (error) { 334 return error; 335 } 336 assert(clientPCB != NULL && clientPCB->magic == REFOS_PCB_MAGIC); 337 338 /* Resume the blocked faulting thread if there is one. */ 339 assert(procServ.unblockClientFaultPID == PID_NULL); 340 procServ.unblockClientFaultPID = clientPCB->pid; 341 return ESUCCESS; 342} 343 344/*! @brief Handles device server device map syscalls. */ 345refos_err_t 346proc_device_map_handler(void *rpc_userptr , seL4_CPtr rpc_window , uint32_t rpc_windowOffset , 347 uint32_t rpc_paddr , uint32_t rpc_size , int rpc_cached) 348{ 349 struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr; 350 struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr; 351 assert(pcb && pcb->magic == REFOS_PCB_MAGIC); 352 353 if (!check_dispatch_caps(m, 0x00000001, 1)) { 354 return EINVALIDPARAM; 355 } 356 357 if ((pcb->systemCapabilitiesMask & PROCESS_PERMISSION_DEVICE_MAP) == 0) { 358 dvprintf("Process needs device map permissions to perform this.\n"); 359 return EACCESSDENIED; 360 } 361 362 /* Retrieve and verify window. */ 363 struct w_window *window = w_get_window(&procServ.windowList, rpc_window - W_BADGE_BASE); 364 if (!window) { 365 ROS_ERROR("window does not exist!\n"); 366 return EINVALIDWINDOW; 367 } 368 369 /* Get the client PCB that owns the given window. */ 370 struct proc_pcb* clientPCB = pid_get_pcb(&procServ.PIDList, window->clientOwnerPID); 371 if (!clientPCB) { 372 ROS_ERROR("invalid window owner!\n"); 373 return EINVALID; 374 } 375 assert(clientPCB->magic == REFOS_PCB_MAGIC); 376 377 return vs_map_device(&clientPCB->vspace, window, rpc_windowOffset, rpc_paddr, rpc_size, 378 rpc_cached ? true : false); 379} 380 381void 382mem_syscall_postaction() 383{ 384 if (procServ.unblockClientFaultPID != PID_NULL) { 385 struct proc_pcb *clientPCB = pid_get_pcb(&procServ.PIDList, procServ.unblockClientFaultPID); 386 if (!clientPCB) { 387 ROS_WARNING("mem_syscall_postaction error: No such PID!"); 388 procServ.unblockClientFaultPID = PID_NULL; 389 return; 390 } 391 392 /* Resume the blocked faulting thread if there is one. */ 393 proc_fault_reply(clientPCB); 394 procServ.unblockClientFaultPID = PID_NULL; 395 } 396} 397