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 <refos/refos.h>
14#include <refos/error.h>
15#include <refos/share.h>
16#include <refos-rpc/proc_common.h>
17#include <refos-rpc/data_client.h>
18#include <refos-rpc/data_client_helper.h>
19#include <refos-util/serv_connect.h>
20#include <utils/arith.h>
21
22#include "dispatch.h"
23#include "../state.h"
24#include "../badge.h"
25
26/*! @file
27    @brief Handles process server client notifications.
28
29    This module is responsible for handling any process server notifications, including client
30    page faults (when acting as pager), dataspace content initialisation calls, and client
31    death notification. Notifications use asynchronous one-way ring buffers, operating on top of
32    an anonymous memory dataspace (ie. the notification buffer).
33*/
34
35/*! @brief Handles client page fault notifications.
36
37    This function handles client page fault notifications from the process server. When we act as
38    the pager, the process server delegates all page faults to us via this notification.
39    We then choose a page to map, and map it.
40
41    @param notification Structure containing the notification message, read from the notification
42                        ring buffer.
43    @return DISPATCH_SUCCESS if success, DISPATCHER_ERROR otherwise.
44*/
45static int
46handle_fileserver_fault(struct proc_notification *notification)
47{
48    int error;
49
50    dvprintf(COLOUR_M "## Fileserv Handling Notification VM fault delegation...\n" COLOUR_RESET);
51    dvprintf("     Label: PROCSERV_NOTIFY_FAULT_DELEGATION\n");
52    dvprintf("     winID: %d\n", notification->arg[0]);
53    dvprintf("     win size: %d\n", notification->arg[1]);
54    dvprintf("     fault address: 0x%x\n", notification->arg[2]);
55    dvprintf("     window base: 0x%x\n", notification->arg[3]);
56    dvprintf("     instruction: 0x%x\n", notification->arg[4]);
57    dvprintf("     permission: 0x%x\n", notification->arg[5]);
58    dvprintf("     pc: 0x%x\n", notification->arg[6]);
59
60    seL4_Word winID = notification->arg[0];
61    seL4_Word winSize = notification->arg[1];
62    seL4_Word faultAddr = notification->arg[2];
63    seL4_Word winBase = notification->arg[3];
64
65    /* Look up the faulting window. */
66    struct dataspace_association_info *dwa = dspace_window_find(&fileServ.dspaceTable, winID);
67    if (!dwa) {
68        ROS_ERROR("File Server does not know about this faulting window.");
69        ROS_ERROR("  This paging delegation request will be ignored. This is most likely a bug.");
70        ROS_ERROR("  Faulting client will be permanently blocked.");
71        assert(!"handle_fileserver_fault bug.");
72        return DISPATCH_SUCCESS;
73    }
74
75    /* Look up the dataspace associated with the faulting window. */
76    struct fs_dataspace* dspace = dspace_get(&fileServ.dspaceTable, dwa->dataspaceID);
77    if (!dspace) {
78        ROS_ERROR("File Server could not find the dataspace associated with the faulting window.");
79        ROS_ERROR("  This paging delegation request will be ignored. This is most likely a bug.");
80        ROS_ERROR("  Faulting client will be permanently blocked.");
81        assert(!"handle_fileserver_fault bug.");
82        return DISPATCH_ERROR;
83    }
84    size_t faultAddrWinOffset = faultAddr - winBase;
85
86    /* Allocate a frame to page client with. */
87    vaddr_t pframe = pager_alloc_frame(&fileServ.pageFrameBlock);
88    if (!pframe) {
89        ROS_ERROR("File Server Out of memory handling VM fault. Paging not implemented.");
90        ROS_ERROR("  Try increasing FILESERVER_MAX_PAGE_FRAMES.");
91        ROS_ERROR("  Faulting client will be permanently blocked.");
92        return DISPATCH_ERROR;
93    }
94    memset((void*) pframe, 0, REFOS_PAGE_SIZE);
95
96    /* Copy any CPIO file content if there is data. */
97    if (dspace->fileData) {
98        /* Round faulting address down to page. */
99        seL4_Word alignedFaultAddr = REFOS_PAGE_ALIGN(faultAddr);
100
101        /* initFrameSkip is to compensate for the case where the window base is not page aligned,
102           and the faulting address is the first page of the window, resulting in a region of
103           page overlap that we can't avoid. Illustration:
104
105                                      winBase   faultAddr
106                                            ���   ���
107                 |_______|_______|_______|__[������������|���������������������|���������������������]_______|_______|
108                                         ���  ������������������ window ������������������������
109                          alignedFaultAddr
110                                         ������������ initFrameSkip = (winBase - alignedFaultAddr)
111        */
112        size_t initFrameSkip = (winBase > alignedFaultAddr) ? (winBase - alignedFaultAddr) : 0;
113
114        /* dataspaceSkipWinOffset is for all cases except the (unsigned window base addr &
115           first page fault) special case. Illustration:
116
117                                      winBase                faultAddr
118                                            ���                ���
119                 |_______|_______|_______|__[������������|���������������������|���������������������]_______|_______|
120                                         ���  ������������������ window ������������������������
121                          alignedFaultAddr
122                                            ������������������������������������������������������ dataspaceSkipWinOffset
123                                              = (alignedFaultAddr - winBase)
124        */
125        size_t dataspaceSkipWinOffset = (winBase > alignedFaultAddr) ?
126                0 : (alignedFaultAddr - winBase);
127
128        /* nbytes is the number of bytes from the offset dataspace start that we should copy into
129           the frame. There are 4 cases here:
130
131            Case 1:
132                 |_______|_______|_______|__[������������|���������������������|���������������������]_______|_______|
133                                            ������������������ nbytes = (REFOS_PAGE_SIZE - initFrameSkip)
134
135            Case 2:
136                 |_______|_______|_______[���������������������|���������������������|���������������������]_______|_______|
137                                                 ��������������������������� nbytes = REFOS_PAGE_SIZE
138
139            Case 3:
140                 |_______|_______|_______[���������������������|���������������������|�����������������]_______|_______|
141                                                         ��������������� nbytes = fileDataSize -
142                                                            dataspaceSkipWinOffset - dataspaceOffset
143                        (�� = indicates there is no more CPIO file data here)
144
145            Case 3:
146                 |_______|_______|_______[���������������������|���������������������|������������]__|_______|_______|
147                                                         ������������������ nbytes =
148                                                                winSize - dataspaceSkipWinOffset
149        */
150        size_t nbytes = MIN(REFOS_PAGE_SIZE - initFrameSkip,
151                dspace->fileDataSize - dataspaceSkipWinOffset - dwa->dataspaceOffset);
152        nbytes = MIN(nbytes, winSize - dataspaceSkipWinOffset);
153
154        /* Check if nbytes is sane. */
155        if ((dspace->fileDataSize - dataspaceSkipWinOffset - dwa->dataspaceOffset >
156                dspace->fileDataSize) ||
157            (dspace->fileDataSize - dataspaceSkipWinOffset > dspace->fileDataSize) ||
158            (dspace->fileDataSize - dwa->dataspaceOffset > dspace->fileDataSize)) {
159            ROS_ERROR("nbytes overflowed.\n");
160            assert(!"nbytes overflowed. Fileserver bug.");
161            pager_free_frame(&fileServ.pageFrameBlock, pframe);
162            return DISPATCH_ERROR;
163        }
164
165        memcpy (
166                (void*) (pframe + initFrameSkip),
167                dspace->fileData + dwa->dataspaceOffset + dataspaceSkipWinOffset,
168                nbytes
169        );
170    }
171
172    /* Now map the frame into the client's vspace window. */
173    dvprintf("    Mapping frame at  0x%x ������������ client 0x%x\n", (uint32_t) pframe,
174            (uint32_t) faultAddr);
175    error = proc_window_map(dwa->objectCap, faultAddrWinOffset, (seL4_Word) pframe);
176    if (error) {
177        ROS_ERROR("File Server Unexpected error while mapping frame!");
178        ROS_ERROR("  Most likely a file server bug.");
179        assert(!"proc_window_map error. Fileserver bug.");
180        pager_free_frame(&fileServ.pageFrameBlock, pframe);
181        return DISPATCH_ERROR;
182    }
183
184    dvprintf("    Successfully mapped frame...\n");
185    return DISPATCH_SUCCESS;
186}
187
188/*! @brief Handles client content init notifications.
189
190    Handles dataspace content init notifications from another dataserver. When we act as a data
191    provider, the external dataserver (eg. process server for anon memory) notifies us with this
192    notification for us to provide the content for it.
193    We find the content that was asked for, and reply to the notification with data_provide_data.
194
195    @param notification Structure containing the notification message, read from the notification
196                        ring buffer.
197    @return DISPATCH_SUCCESS if success, DISPATCHER_ERROR otherwise.
198*/
199static int
200handle_fileserver_content_init(struct proc_notification *notification)
201{
202    dvprintf(COLOUR_M "## Fileserv Handling dspace content initialisation...\n" COLOUR_RESET);
203    dvprintf("    Label: PROCSERV_NOTIFY_CONTENT_INIT\n");
204    dvprintf("    dataID: %d\n", notification->arg[0]);
205    dvprintf("    procDsOffset: %d\n", notification->arg[1]);
206    dvprintf("    winOffset: %d\n", notification->arg[2]);
207
208    seL4_Word dataID = notification->arg[0];
209    seL4_Word destDataspaceOffset = notification->arg[1];
210
211    /* Look up the dataspace --> dataspace association. */
212    struct dataspace_association_info *dda = dspace_external_find(&fileServ.dspaceTable, dataID);
213    if (!dda) {
214        ROS_ERROR("File Server does not know about this content init dspace.");
215        ROS_ERROR("  This paging delegation request will be ignored. This is most likely a bug.");
216        ROS_ERROR("  Faulting client will be permanently blocked.");
217        assert(!"handle_fileserver_fault bug.");
218        return DISPATCH_ERROR;
219    }
220
221    struct fs_dataspace* dspace = dspace_get(&fileServ.dspaceTable, dda->dataspaceID);
222    if (!dspace) {
223        ROS_ERROR("File Server could not find the dataspace for content init.");
224        ROS_ERROR("  This paging delegation request will be ignored. This is most likely a bug.");
225        ROS_ERROR("  Faulting client will be permanently blocked.");
226        assert(!"handle_fileserver_fault bug.");
227        return DISPATCH_ERROR;
228    }
229
230    seL4_Word dataspaceOffset = destDataspaceOffset + dda->dataspaceOffset;
231
232    /* Check the content size to copy. There are 2 cases here: either the size to copy ends
233       with the next page boundary, or is cut short because we've ran out of file data. */
234    size_t contentSize = MIN(dspace->fileDataSize - dataspaceOffset, REFOS_PAGE_SIZE);
235    dvprintf("    Fault file source = 0x%x\n", (uint32_t) dataspaceOffset);
236
237    /* Provide the data back to the process server who notified us. */
238    assert(dataspaceOffset < dspace->fileDataSize);
239    if (dspace->fileData) {
240        int error = data_provide_data(
241                REFOS_PROCSERV_EP, dda->objectCap,
242                destDataspaceOffset, (char*)dspace->fileData + dataspaceOffset,
243                contentSize, &fileServCommon->procServParamBuffer
244        );
245        if (error != ESUCCESS) {
246            ROS_ERROR("File Server could not provide data.");
247            ROS_ERROR("  Faulting client will be permanently blocked.");
248            assert(!"handle_fileserver_fault bug.");
249            return DISPATCH_ERROR;
250        }
251    }
252    dvprintf("      Data ready. Content size is 0x%x\n", contentSize);
253    dvprintf("      Successfully initialised content...\n");
254
255    return DISPATCH_SUCCESS;
256}
257
258/*! @brief Handles client death notifications.
259    @param notification Structure containing the notification message, read from the notification
260                        ring buffer.
261    @return DISPATCH_SUCCESS if success, DISPATCHER_ERROR otherwise.
262*/
263static int
264handle_fileserver_death_notification(struct proc_notification *notification)
265{
266    dprintf(COLOUR_M "## Fileserv Handling death notification...\n" COLOUR_RESET);
267    dprintf("     Label: PROCSERV_NOTIFY_DEATH\n");
268    dprintf("     deathID: %d\n", notification->arg[0]);
269
270    /* Find the client and queue it for deletion. */
271    client_queue_delete(&fileServCommon->clientTable, notification->arg[0]);
272    return DISPATCH_SUCCESS;
273}
274
275int
276dispatch_notification(srv_msg_t *m)
277{
278    if (m->badge != FS_ASYNC_NOTIFY_BADGE) {
279        return DISPATCH_PASS;
280    }
281
282    srv_common_notify_handler_callbacks_t cb = {
283        .handle_server_fault = handle_fileserver_fault,
284        .handle_server_content_init = handle_fileserver_content_init,
285        .handle_server_death_notification = handle_fileserver_death_notification
286    };
287
288    return srv_dispatch_notification(fileServCommon, cb);
289}
290