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