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 "dispatch.h"
14#include "serv_dispatch.h"
15#include "../state.h"
16#include "../badge.h"
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <utils/arith.h>
20#include <fcntl.h>
21#include <refos/error.h>
22#include <refos-rpc/data_server.h>
23#include <refos-rpc/data_client.h>
24#include <refos-util/serv_connect.h>
25#include <refos-util/serv_common.h>
26
27/*! @file
28    @brief Handles CPIO file server dataspace calls.
29
30  This file contains the handlers for dataspace interface syscalls, and exposes the file server's
31  CPIO contents through this interface. The methods here should implement the methods declared in
32  refos-rpc/data_server.h.
33*/
34
35#define CPIO_RAMFS_MAX_CREATED_FILES 64
36#define CPIO_RAMFS_MAX_FILESSIZE 40960
37#define CPIO_RAMFS_MAX_FILENAME 32
38
39/*! @brief Forward declaration of the CPIO archive.
40
41    The CPIO archive is a simple format file archive stored inside a parent program's ELF section.
42    This is a similar idea to something like creating a
43    > const char data[] = { 0x3F, 0xFF, 0x23 ...etc}
44*/
45extern char _cpio_archive[];
46
47/*! @brief Rather hacky minimal ramfs created files.
48
49    This is a rather terrible hack to allow creation of writable files in CPIO fileserver as a sort
50    of minimal crude RamFS. The functionality is nice for portability, although in the future a
51    much better way to do this should be implemented.
52*/
53static char _ramfs_archive[CPIO_RAMFS_MAX_CREATED_FILES][CPIO_RAMFS_MAX_FILESSIZE];
54static char _ramfs_filename[CPIO_RAMFS_MAX_CREATED_FILES][CPIO_RAMFS_MAX_FILENAME];
55static int _ramfs_filesz[CPIO_RAMFS_MAX_CREATED_FILES];
56static int _ramfs_curfile = 0; /* Incrementally allocated files. */
57
58seL4_CPtr
59data_open_handler(void *rpc_userptr , char* rpc_name , int rpc_flags , int rpc_mode , int rpc_size ,
60                  int* rpc_errno)
61{
62    struct srv_client *c = (struct srv_client *) rpc_userptr;
63    assert(c->magic == FS_CLIENT_MAGIC);
64
65    if (!rpc_name) {
66        SET_ERRNO_PTR(rpc_errno, EINVALIDPARAM);
67        return 0;
68    }
69
70    /* Find file data in CPIO. */
71    dprintf("Opening %s...\n", rpc_name);
72    unsigned long fileDataSize = 0;
73    char *fileData = cpio_get_file(_cpio_archive, rpc_name, &fileDataSize);
74    bool fileCreated = false;
75
76    if (fileData && (rpc_flags & O_ACCMODE) != O_RDONLY) {
77        /* CPIO dataspaces require read only. */
78        SET_ERRNO_PTR(rpc_errno, EACCESSDENIED);
79        return 0;
80    }
81
82    if (!fileData) {
83        for (int i = 0; i < _ramfs_curfile; i++) {
84            if (!strcmp(rpc_name, _ramfs_filename[i])) {
85                if ((rpc_flags & O_CREAT)) {
86                    /* Re-create this file. */
87                    memset(_ramfs_archive[i], 0, CPIO_RAMFS_MAX_FILESSIZE);
88                    _ramfs_filesz[i] = 0;
89                }
90                fileData = _ramfs_archive[i];
91                fileDataSize = _ramfs_filesz[i];
92                fileCreated = true;
93                break;
94            }
95        }
96    }
97
98    if (!fileData) {
99        if ((rpc_flags & O_CREAT) == 0) {
100            dprintf("File %s not found!\n", rpc_name);
101            SET_ERRNO_PTR(rpc_errno, EFILENOTFOUND);
102            return 0;
103        }
104        /* Assign new blank RAMFS file. */
105        if (_ramfs_curfile >= CPIO_RAMFS_MAX_CREATED_FILES) {
106            SET_ERRNO_PTR(rpc_errno, EACCESSDENIED);
107            return 0;
108        }
109        dvprintf("Creating new file %s...\n", rpc_name);
110        strncpy(_ramfs_filename[_ramfs_curfile], rpc_name, CPIO_RAMFS_MAX_FILENAME);
111        fileData = _ramfs_archive[_ramfs_curfile++];
112        fileDataSize = 0;
113        fileCreated = true;
114    }
115
116    /* Allocate new dataspace structure. */
117    struct fs_dataspace* nds = dspace_alloc(&fileServ.dspaceTable, c->deathID, fileData,
118        (size_t) fileDataSize, O_RDONLY);
119    if (!nds) {
120        ROS_ERROR("data_open_handler failed to allocate dataspace.");
121        SET_ERRNO_PTR(rpc_errno, ENOMEM);
122        return 0;
123    }
124    nds->fileCreated = fileCreated;
125
126    dvprintf("%s file %s OK ID %d...\n", fileCreated ? "Created" : "Opened", rpc_name, nds->dID);
127    SET_ERRNO_PTR(rpc_errno, ESUCCESS);
128    assert(nds->dataspaceCap);
129    return nds->dataspaceCap;
130}
131
132refos_err_t
133data_close_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd)
134{
135    struct srv_client *c = (struct srv_client *) rpc_userptr;
136    srv_msg_t *m = (srv_msg_t *) c->rpcClient.userptr;
137    assert(c->magic == FS_CLIENT_MAGIC);
138
139    /* Sanity check the dataspace cap. */
140    if (seL4_MessageInfo_get_capsUnwrapped(m->message) != 0x00000001 ||
141        seL4_MessageInfo_get_extraCaps(m->message) != 1) {
142        dprintf("data_close_handler EINVALIDPARAM: bad caps.\n");
143        return EINVALIDPARAM;
144    }
145
146    if (!dspace_get_badge(&fileServ.dspaceTable, rpc_dspace_fd)) {
147        ROS_WARNING("data_close_handler: no such dataspace.");
148        return EINVALIDPARAM;
149    }
150
151    dprintf("Closing dataspace ID %d...\n", rpc_dspace_fd- FS_DSPACE_BADGE_BASE);
152
153    dspace_delete(&fileServ.dspaceTable, rpc_dspace_fd - FS_DSPACE_BADGE_BASE);
154    return ESUCCESS;
155}
156
157int
158data_read_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , uint32_t rpc_offset ,
159                  rpc_buffer_t rpc_buf , uint32_t rpc_count)
160{
161    struct srv_client *c = (struct srv_client *) rpc_userptr;
162    srv_msg_t *m = (srv_msg_t *) c->rpcClient.userptr;
163    assert(c->magic == FS_CLIENT_MAGIC);
164
165    /* Sanity check the dataspace cap. */
166    if (seL4_MessageInfo_get_capsUnwrapped(m->message) != 0x00000001 ||
167        seL4_MessageInfo_get_extraCaps(m->message) != 1) {
168        dprintf("data_read_handler EINVALIDPARAM: bad caps.\n");
169        return EINVALIDPARAM;
170    }
171
172    struct fs_dataspace* dspace = dspace_get_badge(&fileServ.dspaceTable, rpc_dspace_fd);
173    if (!dspace) {
174        ROS_WARNING("data_read_handler: no such dataspace.");
175        return 0;
176    }
177    assert(dspace->magic == FS_DATASPACE_MAGIC);
178    assert(dspace->fileData);
179
180    if (rpc_offset >= dspace->fileDataSize) {
181        return 0;
182    }
183    uint32_t count = MIN(dspace->fileDataSize - rpc_offset, rpc_buf.count);
184    memcpy(rpc_buf.data, dspace->fileData + rpc_offset, count);
185    return count;
186}
187
188int
189data_write_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , uint32_t rpc_offset ,
190                   rpc_buffer_t rpc_buf , uint32_t rpc_count)
191{
192    struct srv_client *c = (struct srv_client *) rpc_userptr;
193    srv_msg_t *m = (srv_msg_t *) c->rpcClient.userptr;
194    assert(c->magic == FS_CLIENT_MAGIC);
195
196    /* Sanity check the dataspace cap. */
197    if (seL4_MessageInfo_get_capsUnwrapped(m->message) != 0x00000001 ||
198        seL4_MessageInfo_get_extraCaps(m->message) != 1) {
199        dprintf("data_write_handler EINVALIDPARAM: bad caps.\n");
200        return -EINVALIDPARAM;
201    }
202
203    struct fs_dataspace* dspace = dspace_get_badge(&fileServ.dspaceTable, rpc_dspace_fd);
204    if (!dspace) {
205        ROS_WARNING("data_write_handler: no such dataspace.");
206        return 0;
207    }
208    assert(dspace->magic == FS_DATASPACE_MAGIC);
209    assert(dspace->fileData);
210
211    if (!dspace->fileCreated) {
212        /* Tried to write to a read only CPIO file. */
213        ROS_WARNING("data_write_handler: Tried to write to a read only CPIO file %d.", dspace->dID);
214        return -EACCESSDENIED;
215    }
216
217    if (rpc_offset + rpc_buf.count > dspace->fileDataSize) {
218        if (rpc_offset + rpc_buf.count > CPIO_RAMFS_MAX_FILESSIZE) {
219            assert(!"File maxsize overflow.");
220            return -ENOMEM;
221        }
222        dspace->fileDataSize = rpc_offset + rpc_buf.count;
223    }
224    for (int i = 0; i < _ramfs_curfile; i++) {
225        if (_ramfs_archive[i] == dspace->fileData) {
226            _ramfs_filesz[i] = dspace->fileDataSize;
227            break;
228        }
229    }
230    memcpy(dspace->fileData + rpc_offset, rpc_buf.data, rpc_buf.count);
231    return rpc_buf.count;
232}
233
234int
235data_getc_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , int rpc_block)
236{
237    assert(!"data_getc_handler not implemented");
238    return EUNIMPLEMENTED;
239}
240
241off_t
242data_lseek_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , off_t rpc_offset , int rpc_whence)
243{
244    assert(!"data_lseek_handler not implemented");
245    return 0;
246}
247
248uint32_t
249data_get_size_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd)
250{
251    struct srv_client *c = (struct srv_client *) rpc_userptr;
252    srv_msg_t *m = (srv_msg_t *) c->rpcClient.userptr;
253    assert(c->magic == FS_CLIENT_MAGIC);
254
255    /* Sanity check the dataspace cap. */
256    if (seL4_MessageInfo_get_capsUnwrapped(m->message) != 0x00000001 ||
257        seL4_MessageInfo_get_extraCaps(m->message) != 1) {
258        dprintf("data_get_size_handler EINVALIDPARAM: bad caps.\n");
259        return 0;
260    }
261
262    struct fs_dataspace* dspace = dspace_get_badge(&fileServ.dspaceTable, rpc_dspace_fd);
263    if (!dspace) {
264        ROS_WARNING("data_get_size_handler: no such dataspace.");
265        return 0;
266    }
267    assert(dspace->magic == FS_DATASPACE_MAGIC);
268
269    return (uint32_t) dspace->fileDataSize;
270}
271
272refos_err_t
273data_expand_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , uint32_t rpc_size)
274{
275    return EUNIMPLEMENTED;
276}
277
278refos_err_t
279data_datamap_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , seL4_CPtr rpc_memoryWindow ,
280                     uint32_t rpc_offset)
281{
282    struct srv_client *c = (struct srv_client *) rpc_userptr;
283    srv_msg_t *m = (srv_msg_t *) c->rpcClient.userptr;
284    assert(c->magic == FS_CLIENT_MAGIC);
285
286    if (seL4_MessageInfo_get_extraCaps(m->message) != 2 ||
287        !(seL4_MessageInfo_get_capsUnwrapped(m->message) & 0x00000001)) {
288        return EINVALIDPARAM;
289    }
290
291    /* Retrieve and validate dataspace badge. */
292    seL4_Word dataspaceBadge = seL4_CapData_Badge_get_Badge(seL4_GetBadge(0));
293    assert(dataspaceBadge == rpc_dspace_fd);
294    struct fs_dataspace* dspace = dspace_get_badge(&fileServ.dspaceTable, dataspaceBadge);
295    if (!dspace) {
296        ROS_ERROR("data_datamap_handler error: no such dataspace.");
297        return EINVALIDPARAM;
298    }
299
300    /* Copy out the memory window cap. Do not printf before the copyout. */
301    seL4_CPtr memoryWindow = rpc_copyout_cptr(rpc_memoryWindow);
302    if (!memoryWindow) {
303        ROS_ERROR("data_datamap_handler error: invalid memory window.");
304        return EINVALIDPARAM;
305    }
306
307    /* Ask the process server to be the pager for this window. */
308    seL4_Word winID;
309    int error = proc_register_as_pager(memoryWindow, fileServCommon->notifyClientFaultDeathAsyncEP,
310                                       &winID);
311    if (error != ESUCCESS) {
312        csfree(memoryWindow);
313        ROS_ERROR("data_datamap_handler error: failed to register as pager.");
314        return EINVALID;
315    }
316
317    /* Set up fileserver window ID bookkeeping. */
318    dprintf("Associating dataspace %d --> windowID %d\n", dspace->dID, winID);
319    error = dspace_window_associate(&fileServ.dspaceTable, winID, dspace->dID, rpc_offset,
320                                    memoryWindow);
321    if (error != ESUCCESS) {
322        // TODO: proc_unregister_as_pager
323        csfree(memoryWindow);
324        return error;
325    }
326
327    return ESUCCESS;
328}
329
330refos_err_t
331data_dataunmap_handler(void *rpc_userptr , seL4_CPtr rpc_memoryWindow)
332{
333    struct srv_client *c = (struct srv_client *) rpc_userptr;
334    srv_msg_t *m = (srv_msg_t *) c->rpcClient.userptr;
335    assert(c->magic == FS_CLIENT_MAGIC);
336
337    if (seL4_MessageInfo_get_extraCaps(m->message) != 1 ||
338        !(seL4_MessageInfo_get_capsUnwrapped(m->message) & 0x00000001)) {
339        return EINVALIDPARAM;
340    }
341
342    /* Copy out the memory window cap. Do not printf before the copyout. */
343    seL4_CPtr memoryWindow = rpc_copyout_cptr(rpc_memoryWindow);
344    if (!memoryWindow) {
345        ROS_ERROR("data_datamap_handler error: invalid memory window.");
346        return EINVALIDPARAM;
347    }
348
349    int winID = proc_window_getID(memoryWindow);
350    if (winID < 0) {
351        ROS_ERROR("data_datamap_handler error: invalid memory window ID.");
352        csfree(memoryWindow);
353        return EINVALIDPARAM;
354    }
355
356    // TODO: proc_unregister_as_pager
357
358    /* Clear any fileserver window ID bookkeeping. */
359    dspace_window_unassociate(&fileServ.dspaceTable, winID);
360    csfree(memoryWindow);
361    return ESUCCESS;
362}
363
364refos_err_t
365data_init_data_handler(void *rpc_userptr , seL4_CPtr rpc_destDataspace ,
366                       seL4_CPtr rpc_srcDataspace , uint32_t rpc_srcDataspaceOffset)
367{
368    struct srv_client *c = (struct srv_client *) rpc_userptr;
369    srv_msg_t *m = (srv_msg_t *) c->rpcClient.userptr;
370    assert(c->magic == FS_CLIENT_MAGIC);
371
372    if (seL4_MessageInfo_get_extraCaps(m->message) != 2 ||
373            seL4_MessageInfo_get_capsUnwrapped(m->message) != 0x00000002) {
374        dvprintf("data_init_data_handler: invalid cap parameters: %d 0x%x !\n",
375                seL4_MessageInfo_get_extraCaps(m->message),
376                seL4_MessageInfo_get_capsUnwrapped(m->message));
377        dvprintf("rpc_destDataspace %d rpc_srcDataspace %d\n", rpc_destDataspace, rpc_srcDataspace)
378        return EINVALIDPARAM;
379    }
380
381    /* Retrieve and validate dataspace badge. */
382    struct fs_dataspace* dspace = dspace_get_badge(&fileServ.dspaceTable, rpc_srcDataspace);
383    if (!dspace) {
384        ROS_ERROR("data_init_data_handler error: no such dest dataspace.");
385        return EINVALIDPARAM;
386    }
387
388    /* Copyout the source dataspace cap. Do not printf before the copyout. */
389    seL4_CPtr destDataspace = rpc_copyout_cptr(rpc_destDataspace);
390    if (!destDataspace) {
391        ROS_ERROR("data_init_data_handler error: could not copyout src dspace cap.");
392        return ENOMEM;
393    }
394
395    /* Notify process server that we want to provide data for this dataspace. */
396    uint32_t dataID = (uint32_t) -1;
397    int error = data_have_data(REFOS_PROCSERV_EP, destDataspace, fileServCommon->notifyClientFaultDeathAsyncEP, &dataID);
398    if (error != ESUCCESS || dataID == -1) {
399        csfree_delete(destDataspace);
400        ROS_ERROR("data_init_data_handler error: data_have_data failed.");
401        return EINVALID;
402    }
403
404    /* Set up fileserver dataspace ID bookkeeping. */
405    dprintf("Associating dataspace %d --> external dataspace %d\n", dspace->dID, dataID);
406    error = dspace_external_associate(&fileServ.dspaceTable, dataID, dspace->dID,
407                                      rpc_srcDataspaceOffset, destDataspace);
408    if (error != ESUCCESS) {
409        ROS_ERROR("Failed to associate dataspace.");
410        data_unhave_data(REFOS_PROCSERV_EP, destDataspace);
411        csfree_delete(destDataspace);
412        return error;
413    }
414
415    return ESUCCESS;
416}
417
418refos_err_t
419data_have_data_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , seL4_CPtr rpc_faultNotifyEP ,
420                       uint32_t* rpc_dataID)
421{
422    ROS_WARNING("CPIO Fileserver does not support data_have_data!");
423    return EUNIMPLEMENTED;
424}
425
426refos_err_t
427data_unhave_data_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd)
428{
429    ROS_WARNING("CPIO Fileserver does not support data_unhave_data!");
430    return EUNIMPLEMENTED;
431}
432
433refos_err_t
434data_provide_data_from_parambuffer_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd ,
435                                           uint32_t rpc_offset , uint32_t rpc_contentSize)
436{
437    ROS_WARNING("CPIO Fileserver does not support data_provide_data!");
438    return EUNIMPLEMENTED;
439}
440
441int
442check_dispatch_data(srv_msg_t *m, void **userptr)
443{
444    if (m->badge == SRV_UNBADGED) {
445        return DISPATCH_PASS;
446    }
447    return check_dispatch_interface(m, userptr, RPC_DATA_LABEL_MIN, RPC_DATA_LABEL_MAX);
448}
449