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