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/error.h> 14#include <refos-io/internal_state.h> 15#include <refos-io/ipc_state.h> 16#include <refos-io/filetable.h> 17#include <refos-util/init.h> 18#include <refos-rpc/data_client.h> 19#include <refos-rpc/data_client_helper.h> 20#include <utils/arith.h> 21#include <autoconf.h> 22 23#include <stdio.h> 24#include <sys/uio.h> 25#include <limits.h> 26#include <errno.h> 27#include <sel4/sel4.h> 28#include <stdarg.h> 29#include <fcntl.h> 30#include <refos-util/dprintf.h> 31 32#define STDIN_FD 0 33#define STDOUT_FD 1 34#define STDERR_FD 2 35 36#define REFOS_SYSIO_MAX_PATHLEN 256 37 38static size_t 39sys_platform_stdout_write(void *data, size_t count) 40{ 41 char *cdata = data; 42 43#if defined(SEL4_DEBUG_KERNEL) && defined(CONFIG_REFOS_SYS_FORCE_DEBUGPUTCHAR) 44 for (size_t i = 0; i < count; i++) { 45 seL4_DebugPutChar(cdata[i]); 46 } 47#else 48 49 if (refosIOState.stdioWriteOverride != NULL) { 50 /* Use overridden write function. */ 51 return refosIOState.stdioWriteOverride(data, count); 52 } 53 54 /* Use serial dataspace on Console server. */ 55 if (refosIOState.stdioDataspace && refosIOState.stdioSession.serverSession) { 56 refosio_internal_save_IPC_buffer(); 57 for (size_t i = 0; i < count;) { 58 int c = MIN(REFOS_DEFAULT_DSPACE_IPC_MAXLEN, count - i); 59 int n = data_write(refosIOState.stdioSession.serverSession, refosIOState.stdioDataspace, 60 0, &cdata[i], c); 61 if (!n) { 62 /* An error occured. */ 63 refosio_internal_restore_IPC_buffer(); 64 return i; 65 } 66 67 i += n; 68 } 69 refosio_internal_restore_IPC_buffer(); 70 } 71 72#endif 73 74 return count; 75} 76 77static size_t 78sys_platform_stdin_read(void *data, size_t count) 79{ 80 assert(data && count); 81 int c = refos_getc(); 82 if (c < 0) { 83 return 0; 84 } 85 char cc = (char) c; 86 memcpy(data, &cc, sizeof(char)); 87 return sizeof(char); 88} 89 90/* Writev syscall implementation for muslc. Only implemented for stdin and stdout. */ 91static long 92_sys_writev(int fildes, struct iovec *iov, int iovcnt) 93{ 94 long long sum = 0; 95 ssize_t ret = 0; 96 97 /* The iovcnt argument is valid if greater than 0 and less than or equal to IOV_MAX. */ 98 if (iovcnt <= 0 || iovcnt > IOV_MAX) 99 return -EINVAL; 100 101 /* The sum of iov_len is valid if less than or equal to SSIZE_MAX i.e. cannot overflow 102 a ssize_t. */ 103 for (int i = 0; i < iovcnt; i++) { 104 if (iov[i].iov_len < 0) 105 return -EINVAL; 106 107 sum += (long long)iov[i].iov_len; 108 if (sum > SSIZE_MAX) 109 return -EINVAL; 110 } 111 112 /* If all the iov_len members in the array are 0, return 0. */ 113 if (!sum) 114 return 0; 115 116 /* Write the buffer to console if the fd is for stdout or stderr. */ 117 if (fildes == STDOUT_FD || fildes == STDERR_FD) { 118 for (int i = 0; i < iovcnt; i++) { 119 ret += sys_platform_stdout_write(iov[i].iov_base, iov[i].iov_len); 120 } 121 } else if (fildes == STDIN_FD) { 122 /* Can't write to stdin. */ 123 assert(!"Can't write to stdin."); 124 return -EACCES; 125 } else { 126 for (int i = 0; i < iovcnt; i++) { 127 if (iov[i].iov_len == 0) continue; 128 int offset = 0; 129 130 while (offset < iov[i].iov_len) { 131 int nc = filetable_write(&refosIOState.fdTable, fildes, iov[i].iov_base + offset, 132 iov[i].iov_len - offset); 133 if (nc >= 0) { 134 assert(nc <= iov[i].iov_len - offset); 135 offset += nc; 136 ret += nc; 137 } else { 138 ret = -EFAULT; 139 break; 140 } 141 } 142 143 if (ret < 0) { 144 break; 145 } 146 } 147 } 148 149 return ret; 150} 151 152long 153sys_writev(va_list ap) 154{ 155 int fildes = va_arg(ap, int); 156 struct iovec *iov = va_arg(ap, struct iovec *); 157 int iovcnt = va_arg(ap, int); 158 return _sys_writev(fildes, iov, iovcnt); 159} 160 161long 162sys_write(va_list ap) { 163 int fildes = va_arg(ap, int); 164 void* buf = va_arg(ap, void*); 165 size_t count = va_arg(ap, size_t); 166 167 struct iovec iov = { 168 .iov_base = buf, 169 .iov_len = count 170 }; 171 172 return _sys_writev(fildes, &iov, 1); 173} 174 175static long 176_sys_readv(int fildes, struct iovec *iov, int iovcnt) 177{ 178 long long sum = 0; 179 ssize_t ret = 0; 180 181 /* The iovcnt argument is valid if greater than 0 and less than or equal to IOV_MAX. */ 182 if (iovcnt <= 0 || iovcnt > IOV_MAX) 183 return -EINVAL; 184 185 /* The sum of iov_len is valid if less than or equal to SSIZE_MAX i.e. cannot overflow 186 a ssize_t. */ 187 for (int i = 0; i < iovcnt; i++) { 188 if (iov[i].iov_len < 0) 189 return -EINVAL; 190 191 sum += (long long)iov[i].iov_len; 192 if (sum > SSIZE_MAX) 193 return -EINVAL; 194 } 195 196 /* If all the iov_len members in the array are 0, return 0. */ 197 if (!sum) 198 return 0; 199 200 /* Read the iov buffers. */ 201 if (fildes == STDIN_FD) { 202 /* Read from STDIN. */ 203 for (int i = 0; i < iovcnt; i++) { 204 if (iov[i].iov_len == 0) continue; 205 ret += sys_platform_stdin_read(iov[i].iov_base, iov[i].iov_len); 206 break; 207 } 208 return ret; 209 } 210 211 /* Read from dataspace file. */ 212 for (int i = 0; i < iovcnt; i++) { 213 if (iov[i].iov_len == 0) continue; 214 215 int nc = filetable_read(&refosIOState.fdTable, fildes, iov[i].iov_base, iov[i].iov_len); 216 if (nc < 0) { 217 return -1; 218 } else if (nc < iov[i].iov_len) { 219 ret += nc; 220 break; 221 } 222 ret += nc; 223 } 224 225 return ret; 226} 227 228long 229sys_readv(va_list ap) 230{ 231 int fildes = va_arg(ap, int); 232 struct iovec *iov = va_arg(ap, struct iovec *); 233 int iovcnt = va_arg(ap, int); 234 return _sys_readv(fildes, iov, iovcnt); 235} 236 237long 238sys_read(va_list ap) { 239 int fildes = va_arg(ap, int); 240 void* buf = va_arg(ap, void*); 241 size_t count = va_arg(ap, size_t); 242 243 struct iovec iov = { 244 .iov_base = buf, 245 .iov_len = count 246 }; 247 248 return _sys_readv(fildes, &iov, 1); 249} 250 251long 252sys_open(va_list ap) 253{ 254 char *pathname = va_arg(ap, char*); 255 int flags = va_arg(ap, int); 256 int fd = -1; 257 static char tempBufferPath[REFOS_SYSIO_MAX_PATHLEN]; 258 259 /* Handle the PWD environment variable. */ 260 char *pwd = getenv("PWD"); 261 if (pwd && strlen(pwd) > 0) { 262 snprintf(tempBufferPath, REFOS_SYSIO_MAX_PATHLEN, "%s%s", getenv("PWD"), pathname); 263 pathname = tempBufferPath; 264 } 265 266 /* Open dataspace file. */ 267 fd = filetable_dspace_open(&refosIOState.fdTable, pathname, flags, 0, 0x1000); 268 switch (ROS_ERRNO()) { 269 case ESUCCESS: break; 270 case EINVALID: return -EFAULT; 271 case EINVALIDPARAM: return -EACCES; 272 case EFILENOTFOUND: return -EMFILE; 273 case ESERVERNOTFOUND: return -EMFILE; 274 default: return -EFAULT; 275 } 276 assert(fd); 277 278 return fd; 279} 280 281long 282_sys_lseek(int fildes, off_t offset, int whence) 283{ 284 int newOffset = (int) offset; 285 if (fildes == STDOUT_FD || fildes == STDERR_FD || fildes == STDIN_FD) { 286 /* lseek for STDOUT / STDIN / STDERR makes no sense. */ 287 return newOffset; 288 } 289 290 /* Perform lseek on filetable. */ 291 int error = filetable_lseek(&refosIOState.fdTable, fildes, &newOffset, whence); 292 if (error != ESUCCESS) { 293 return -EFAULT; 294 } 295 return newOffset; 296} 297 298long 299sys_lseek(va_list ap) 300{ 301 int fildes = va_arg(ap, int); 302 off_t offset = va_arg(ap, int); 303 int whence = va_arg(ap, int); 304 return _sys_lseek(fildes, offset , whence); 305} 306 307long 308sys__llseek(va_list ap) 309{ 310 int fildes = va_arg(ap, int); 311 unsigned long offset_high = va_arg(ap, unsigned long); 312 unsigned long offset_low = va_arg(ap, unsigned long); 313 off_t *result = va_arg(ap, off_t*); 314 int whence = va_arg(ap, int); 315 off_t seek_distance; 316 317 if (!result) { 318 return -1; 319 } 320 321 int64_t seek_distance64 = (int64_t)(((uint64_t)offset_high << 32) | (uint64_t)offset_low); 322 if (seek_distance64 > (int64_t)INT_MAX || seek_distance64 < (int64_t)INT_MIN) { 323 /* This number cannot be represented by signed 32-bit integers, so return error. 324 If this ever presents a problem, consider extending this to use 64-bit integers. */ 325 return -1; 326 } 327 328 seek_distance = (off_t)seek_distance64; 329 330 (*result) = (off_t) _sys_lseek(fildes, seek_distance, whence); 331 return 0; 332} 333 334long 335sys_ioctl(va_list ap) 336{ 337 /* muslc does some ioctl to stdout, so just allow these to silently go through */ 338 return 0; 339} 340 341long 342sys_close(va_list ap) 343{ 344 int fildes = va_arg(ap, int); 345 if (fildes == STDOUT_FD || fildes == STDERR_FD || fildes == STDIN_FD) { 346 /* close for STDOUT / STDIN / STDERR makes no sense. */ 347 return 0; 348 } 349 350 /* Perform close on filetable. */ 351 int error = filetable_close(&refosIOState.fdTable, fildes); 352 if (error != ESUCCESS) { 353 return -EIO; 354 } 355 return 0; 356} 357