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, 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(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 printf("do_sdevio: verify_grant failed\n"); 76 return EPERM; 77 } 78 if(!isokendpt(newep, &proc_nr)) 79 return(EINVAL); 80 destproc = proc_addr(proc_nr); 81 vir_buf = newoffset; 82 } else { 83 if(proc_nr != _ENDPOINT_P(caller->p_endpoint)) 84 { 85 printf("do_sdevio: unsafe sdevio by %d in %d denied\n", 86 caller->p_endpoint, proc_nr_e); 87 return EPERM; 88 } 89 /* Get and check physical address. */ 90 vir_buf = m_ptr->m_lsys_krn_sys_sdevio.vec_addr; 91 destproc = proc_addr(proc_nr); 92 } 93 /* current process must be target for phys_* to be OK */ 94 95 switch_address_space(destproc); 96 97 switch (req_type) 98 { 99 case _DIO_BYTE: size= 1; break; 100 case _DIO_WORD: size= 2; break; 101 case _DIO_LONG: size= 4; break; 102 default: size= 4; break; /* Be conservative */ 103 } 104 105 privp= priv(caller); 106 if (privp && privp->s_flags & CHECK_IO_PORT) 107 { 108 port= m_ptr->m_lsys_krn_sys_sdevio.port; 109 nr_io_range= privp->s_nr_io_range; 110 for (i= 0, iorp= privp->s_io_tab; i<nr_io_range; i++, iorp++) 111 { 112 if (port >= iorp->ior_base && port+size-1 <= iorp->ior_limit) 113 break; 114 } 115 if (i >= nr_io_range) 116 { 117 printf( 118 "do_sdevio: I/O port check failed for proc %d, port 0x%x\n", 119 m_ptr->m_source, port); 120 retval = EPERM; 121 goto return_error; 122 } 123 } 124 125 if (port & (size-1)) 126 { 127 printf("do_devio: unaligned port 0x%x (size %d)\n", port, size); 128 retval = EPERM; 129 goto return_error; 130 } 131 132 /* Perform device I/O for bytes and words. Longs are not supported. */ 133 if (req_dir == _DIO_INPUT) { 134 switch (req_type) { 135 case _DIO_BYTE: phys_insb(port, vir_buf, count); break; 136 case _DIO_WORD: phys_insw(port, vir_buf, count); break; 137 default: 138 retval = EINVAL; 139 goto return_error; 140 } 141 } else if (req_dir == _DIO_OUTPUT) { 142 switch (req_type) { 143 case _DIO_BYTE: phys_outsb(port, vir_buf, count); break; 144 case _DIO_WORD: phys_outsw(port, vir_buf, count); break; 145 default: 146 retval = EINVAL; 147 goto return_error; 148 } 149 } 150 else { 151 retval = EINVAL; 152 goto return_error; 153 } 154 retval = OK; 155 156 return_error: 157 /* switch back to the address of the process which made the call */ 158 switch_address_space(caller); 159 return retval; 160 } 161 162 #endif /* USE_SDEVIO */ 163