1 /* The kernel call implemented in this file: 2 * m_type: SYS_SDEVIO 3 * 4 * The parameters for this kernel call are: 5 * m_lsys_krn_sys_sdevio.request (request input or output) 6 * m_lsys_krn_sys_sdevio.port (port to read/ write) 7 * m_lsys_krn_sys_sdevio.vec_addr (virtual address of buffer, or grant ID) 8 * m_lsys_krn_sys_sdevio.vec_size (number of elements) 9 * m_lsys_krn_sys_sdevio.vec_endpt (process where buffer is) 10 * m_lsys_krn_sys_sdevio.offset (offset into the grant) 11 */ 12 13 #include "kernel/system.h" 14 #include <minix/devio.h> 15 #include <minix/endpoint.h> 16 17 #include "arch_proto.h" 18 19 #if USE_SDEVIO 20 21 /*===========================================================================* 22 * do_sdevio * 23 *===========================================================================*/ 24 int do_sdevio(struct proc * caller, message *m_ptr) 25 { 26 vir_bytes newoffset; 27 endpoint_t newep; 28 int proc_nr; 29 endpoint_t proc_nr_e = m_ptr->m_lsys_krn_sys_sdevio.vec_endpt; 30 vir_bytes count = m_ptr->m_lsys_krn_sys_sdevio.vec_size; 31 long port = m_ptr->m_lsys_krn_sys_sdevio.port; 32 phys_bytes vir_buf; 33 int i, r, req_type, req_dir, size, nr_io_range; 34 struct priv *privp; 35 struct io_range *iorp; 36 struct proc *destproc; 37 int retval; 38 39 /* Allow safe copies and accesses to SELF */ 40 if ((m_ptr->m_lsys_krn_sys_sdevio.request & _DIO_SAFEMASK) != _DIO_SAFE && 41 proc_nr_e != SELF) 42 { 43 static int first= 1; 44 if (first) 45 { 46 first= 0; 47 printf("do_sdevio: for %d, req %d\n", 48 m_ptr->m_source, m_ptr->m_lsys_krn_sys_sdevio.request); 49 } 50 } 51 52 /* Check if process endpoint is OK. 53 * A driver may directly provide a pointer to a buffer at the user-process 54 * that initiated the device I/O. Kernel processes, of course, are denied. 55 */ 56 if (proc_nr_e == SELF) 57 okendpt(caller->p_endpoint, &proc_nr); 58 else 59 if(!isokendpt(proc_nr_e, &proc_nr)) 60 return(EINVAL); 61 if (iskerneln(proc_nr)) return(EPERM); 62 63 /* Extract direction (in or out) and type (size). */ 64 req_dir = m_ptr->m_lsys_krn_sys_sdevio.request & _DIO_DIRMASK; 65 req_type = m_ptr->m_lsys_krn_sys_sdevio.request & _DIO_TYPEMASK; 66 67 /* Check for 'safe' variants. */ 68 if((m_ptr->m_lsys_krn_sys_sdevio.request & _DIO_SAFEMASK) == _DIO_SAFE) { 69 /* Map grant address to physical address. */ 70 if((r=verify_grant(proc_nr_e, caller->p_endpoint, 71 m_ptr->m_lsys_krn_sys_sdevio.vec_addr, count, 72 req_dir == _DIO_INPUT ? CPF_WRITE : CPF_READ, 73 m_ptr->m_lsys_krn_sys_sdevio.offset, &newoffset, &newep, 74 NULL)) != OK) { 75 if(r == ENOTREADY) return r; 76 printf("do_sdevio: verify_grant failed\n"); 77 return EPERM; 78 } 79 if(!isokendpt(newep, &proc_nr)) 80 return(EINVAL); 81 destproc = proc_addr(proc_nr); 82 vir_buf = newoffset; 83 } else { 84 if(proc_nr != _ENDPOINT_P(caller->p_endpoint)) 85 { 86 printf("do_sdevio: unsafe sdevio by %d in %d denied\n", 87 caller->p_endpoint, proc_nr_e); 88 return EPERM; 89 } 90 /* Get and check physical address. */ 91 vir_buf = m_ptr->m_lsys_krn_sys_sdevio.vec_addr; 92 destproc = proc_addr(proc_nr); 93 } 94 /* current process must be target for phys_* to be OK */ 95 96 switch_address_space(destproc); 97 98 switch (req_type) 99 { 100 case _DIO_BYTE: size= 1; break; 101 case _DIO_WORD: size= 2; break; 102 case _DIO_LONG: size= 4; break; 103 default: size= 4; break; /* Be conservative */ 104 } 105 106 privp= priv(caller); 107 if (privp && privp->s_flags & CHECK_IO_PORT) 108 { 109 port= m_ptr->m_lsys_krn_sys_sdevio.port; 110 nr_io_range= privp->s_nr_io_range; 111 for (i= 0, iorp= privp->s_io_tab; i<nr_io_range; i++, iorp++) 112 { 113 if (port >= iorp->ior_base && port+size-1 <= iorp->ior_limit) 114 break; 115 } 116 if (i >= nr_io_range) 117 { 118 printf( 119 "do_sdevio: I/O port check failed for proc %d, port 0x%x\n", 120 m_ptr->m_source, port); 121 retval = EPERM; 122 goto return_error; 123 } 124 } 125 126 if (port & (size-1)) 127 { 128 printf("do_devio: unaligned port 0x%x (size %d)\n", port, size); 129 retval = EPERM; 130 goto return_error; 131 } 132 133 /* Perform device I/O for bytes and words. Longs are not supported. */ 134 if (req_dir == _DIO_INPUT) { 135 switch (req_type) { 136 case _DIO_BYTE: phys_insb(port, vir_buf, count); break; 137 case _DIO_WORD: phys_insw(port, vir_buf, count); break; 138 default: 139 retval = EINVAL; 140 goto return_error; 141 } 142 } else if (req_dir == _DIO_OUTPUT) { 143 switch (req_type) { 144 case _DIO_BYTE: phys_outsb(port, vir_buf, count); break; 145 case _DIO_WORD: phys_outsw(port, vir_buf, count); break; 146 default: 147 retval = EINVAL; 148 goto return_error; 149 } 150 } 151 else { 152 retval = EINVAL; 153 goto return_error; 154 } 155 retval = OK; 156 157 return_error: 158 /* switch back to the address of the process which made the call */ 159 switch_address_space(caller); 160 return retval; 161 } 162 163 #endif /* USE_SDEVIO */ 164