1/* 2 * Copyright 2017, 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(DATA61_BSD) 11 */ 12 13 14#include <sys/cdefs.h> 15#include <sys/param.h> 16#include <sys/cpu.h> 17#include <sys/kernel.h> 18#include <sys/module.h> 19#include <rump-sys/vfs.h> 20#include <sys/ioctl.h> 21#include <sys/termios.h> 22 23#include <sys/stat.h> 24#include <rump-sys/kern.h> 25#include "sel4_stdio.h" 26 27/* 28 This file implements stdio device files that are accessed through the 29 files /dev/stdin, /dev/stdout, /dev/stderr. Its purpose is to provide input and output over an underlying 30 circular buffer using seL4 notification objects for signalling. 31 Currently read and write are supported. Read will block if 32 no input data is available. There is currently no way to check if data is available 33 to be read without blocking. 34*/ 35 36MODULE(MODULE_CLASS_DRIVER, sel4_stdio, NULL); 37 38static struct rumpuser_cv *cvp_serial; 39static struct rumpuser_mtx *mtxp_serial; 40 41 42/* Interrupt handler, it signals a thread waiting for data available in sel4_stdio_read */ 43static void get_char_handler(void) { 44 rumpuser_cv_signal(cvp_serial); 45} 46 47static int 48sel4_stdio_open(dev_t dev, int flag, int mode, struct lwp *l) 49{ 50 static bool open_once[RR_NUMIO] = {0}; 51 52 if (minor(dev) < 0 || minor(dev) >= RR_NUMIO) { 53 panic("sel4_stdio: invalid minor device number"); 54 } 55 if (open_once[minor(dev)]) { 56 panic("sel4_stdio only supports each file being opened once and never closed"); 57 } 58 open_once[minor(dev)] = true; 59 60 if (rumpcomp_sel4_stdio_init(minor(dev))) { 61 aprint_error("Failed to init sel4_stdio"); 62 return -1; 63 } 64 65 /* If stdin */ 66 if (minor(dev) == RR_STDIN) { 67 /* Create condition variable and mutex for interrupt handling syncronisation */ 68 rumpuser_cv_init(&cvp_serial); 69 rumpuser_mutex_init(&mtxp_serial, RUMPUSER_MTX_SPIN); 70 rumpuser_mutex_enter(mtxp_serial); 71 /* Register input interrupt handler */ 72 rumpcomp_register_handler(get_char_handler); 73 } 74 return 0; 75} 76 77/* TODO Deal with carrige return stuff */ 78static int 79sel4_stdio_read(dev_t dev, struct uio *uio, int flag) 80{ 81 char *buf; 82 size_t len, n; 83 int error = 0; 84 if (minor(dev) != RR_STDIN) { 85 aprint_error("Only stdin supports read"); 86 return -1; 87 } 88 89 /* Allocate buffer for read */ 90 buf = kmem_alloc(PAGE_SIZE, KM_SLEEP); 91 if (buf == NULL) { 92 aprint_error("kmem_alloc returned null"); 93 return -1; 94 } 95 n = 0; 96 len = min(PAGE_SIZE, uio->uio_resid); 97 /* Try and dequeue characters from the buffer 98 If no characters are available, block on a condition variable 99 When characters become available, wake up and read out characters. 100 Blocking only occurs if no characters have been read */ 101 while (n < len ) { 102 n += rumpcomp_sel4_stdio_gets(minor(dev), buf, len); 103 if (n == 0) { 104 rumpuser_cv_wait(cvp_serial, mtxp_serial); 105 } else { 106 break; 107 } 108 } 109 /* Move read characters into user buffers */ 110 error = uiomove(buf, n, uio); 111 112 kmem_free(buf, PAGE_SIZE); 113 114 return error; 115} 116 117static int 118sel4_stdio_write(dev_t dev, struct uio *uio, int flag) 119{ 120 121 char *buf; 122 size_t len; 123 int error = 0; 124 if (minor(dev) == RR_STDIN) { 125 aprint_error("stdin doesn't support write"); 126 return -1; 127 } 128 /* Allocate write buffer */ 129 buf = kmem_alloc(PAGE_SIZE, KM_SLEEP); 130 if (buf == NULL) { 131 aprint_error("kmem_alloc returned null"); 132 return -1; 133 } 134 135 while (uio->uio_resid > 0) { 136 len = min(PAGE_SIZE, uio->uio_resid); 137 error = uiomove(buf, len, uio); 138 if (error) 139 break; 140 rumpcomp_sel4_stdio_puts(minor(dev), buf, len); 141 } 142 kmem_free(buf, PAGE_SIZE); 143 144 return error; 145 146} 147 148static int 149sel4_stdio_ioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) 150{ 151 /* We do not support tty */ 152 if (cmd == TIOCGETA) 153 return 0; 154 155 return ENOTTY; 156 157} 158 159static int 160sel4_stdio_poll(dev_t dev, int events, struct lwp *l) 161{ 162 /* No poll currently supported */ 163 aprint_normal("poll called.\n"); 164 165 return -1; 166} 167 168static int 169sel4_stdio_kqfilter(dev_t dev, struct knote *kn) 170{ 171 /* no kqfilter currently supported */ 172 aprint_normal("ioctl kqfilter.\n"); 173 174 return -1; 175} 176 177 178const struct cdevsw sel4_stdio = { 179 .d_open = sel4_stdio_open, 180 .d_close = nullclose, 181 .d_read = sel4_stdio_read, 182 .d_write = sel4_stdio_write, 183 .d_ioctl = sel4_stdio_ioctl, 184 .d_stop = nostop, 185 .d_tty = notty, 186 .d_poll = sel4_stdio_poll, 187 .d_mmap = nommap, 188 .d_kqfilter = sel4_stdio_kqfilter, 189 .d_discard = nodiscard, 190 .d_flag = D_TTY 191}; 192 193 194 195static int 196sel4_stdio_modcmd(modcmd_t cmd, void *arg) 197{ 198 devmajor_t bmaj, cmaj; 199 200 bmaj = cmaj = -1; 201 /* Register cdevsw with cmaj driver number. */ 202 FLAWLESSCALL(devsw_attach("sel4_stdio", NULL, &bmaj, &sel4_stdio, &cmaj)); 203 /* Create character device node using cmaj number we were assigned */ 204 FLAWLESSCALL(rump_vfs_makeonedevnode(S_IFCHR, "/dev/stdin", cmaj, RR_STDIN)); 205 FLAWLESSCALL(rump_vfs_makeonedevnode(S_IFCHR, "/dev/stdout", cmaj, RR_STDOUT)); 206 FLAWLESSCALL(rump_vfs_makeonedevnode(S_IFCHR, "/dev/stderr", cmaj, RR_STDERR)); 207 /* Print to init output */ 208 aprint_normal("sel4_stdio: Created stdio device files.\n"); 209 return 0; 210}