xref: /minix/minix/kernel/system/do_vdevio.c (revision 03ac74ed)
1 /* The kernel call implemented in this file:
2  *   m_type:	SYS_VDEVIO
3  *
4  * The parameters for this kernel call are:
5  *    m_lsys_krn_sys_vdevio.request	(request input or output)
6  *    m_lsys_krn_sys_vdevio.vec_addr	(pointer to port/ value pairs)
7  *    m_lsys_krn_sys_vdevio.vec_size	(number of ports to read or write)
8  */
9 
10 #include "kernel/system.h"
11 #include <minix/devio.h>
12 #include <minix/endpoint.h>
13 
14 #if USE_VDEVIO
15 
16 /* Buffer for SYS_VDEVIO to copy (port,value)-pairs from/ to user. */
17 static char vdevio_buf[VDEVIO_BUF_SIZE];
18 static pvb_pair_t * const pvb = (pvb_pair_t *) vdevio_buf;
19 static pvw_pair_t * const pvw = (pvw_pair_t *) vdevio_buf;
20 static pvl_pair_t * const pvl = (pvl_pair_t *) vdevio_buf;
21 
22 /*===========================================================================*
23  *			        do_vdevio                                    *
24  *===========================================================================*/
25 int do_vdevio(struct proc * caller, message * m_ptr)
26 {
27 /* Perform a series of device I/O on behalf of a non-kernel process. The
28  * I/O addresses and I/O values are fetched from and returned to some buffer
29  * in user space. The actual I/O is wrapped by lock() and unlock() to prevent
30  * that I/O batch from being interrupted.
31  * This is the counterpart of do_devio, which performs a single device I/O.
32  */
33   int vec_size;               /* size of vector */
34   int io_in;                  /* true if input */
35   size_t bytes;               /* # bytes to be copied */
36   port_t port;
37   int i, j, io_size, nr_io_range;
38   int io_dir, io_type;
39   struct priv *privp;
40   struct io_range *iorp;
41   int r;
42 
43   /* Get the request, size of the request vector, and check the values. */
44   io_dir = m_ptr->m_lsys_krn_sys_vdevio.request & _DIO_DIRMASK;
45   io_type = m_ptr->m_lsys_krn_sys_vdevio.request & _DIO_TYPEMASK;
46   if (io_dir == _DIO_INPUT) io_in = TRUE;
47   else if (io_dir == _DIO_OUTPUT) io_in = FALSE;
48   else return(EINVAL);
49   if ((vec_size = m_ptr->m_lsys_krn_sys_vdevio.vec_size) <= 0) return(EINVAL);
50   switch (io_type) {
51       case _DIO_BYTE:
52 	bytes = vec_size * sizeof(pvb_pair_t);
53 	io_size= sizeof(u8_t);
54 	break;
55       case _DIO_WORD:
56 	bytes = vec_size * sizeof(pvw_pair_t);
57 	io_size= sizeof(u16_t);
58 	break;
59       case _DIO_LONG:
60 	bytes = vec_size * sizeof(pvl_pair_t);
61 	io_size= sizeof(u32_t);
62 	break;
63       default:  return(EINVAL);   /* check type once and for all */
64   }
65   if (bytes > sizeof(vdevio_buf))  return(E2BIG);
66 
67   /* Copy (port,value)-pairs from user. */
68   if((r=data_copy(caller->p_endpoint, m_ptr->m_lsys_krn_sys_vdevio.vec_addr,
69     KERNEL, (vir_bytes) vdevio_buf, bytes)) != OK)
70 	return r;
71 
72   privp= priv(caller);
73   if (privp && (privp->s_flags & CHECK_IO_PORT))
74   {
75 	/* Check whether the I/O is allowed */
76 	nr_io_range= privp->s_nr_io_range;
77 	for (i=0; i<vec_size; i++)
78 	{
79 		switch (io_type) {
80 		case _DIO_BYTE: port= pvb[i].port; break;
81 		case _DIO_WORD: port= pvw[i].port; break;
82 		default:	port= pvl[i].port; break;
83 		}
84 		for (j= 0, iorp= privp->s_io_tab; j<nr_io_range; j++, iorp++)
85 		{
86 			if (port >= iorp->ior_base &&
87 				port+io_size-1 <= iorp->ior_limit)
88 			{
89 				break;
90 			}
91 		}
92 		if (j >= nr_io_range)
93 		{
94 			printf(
95 		"do_vdevio: I/O port check failed for proc %d, port 0x%x\n",
96 				caller->p_endpoint, port);
97 			return EPERM;
98 		}
99 	}
100   }
101 
102   /* Perform actual device I/O for byte, word, and long values */
103   switch (io_type) {
104   case _DIO_BYTE: 					 /* byte values */
105       if (io_in) for (i=0; i<vec_size; i++)
106 		pvb[i].value = inb( pvb[i].port);
107       else      for (i=0; i<vec_size; i++)
108 		outb( pvb[i].port, pvb[i].value);
109       break;
110   case _DIO_WORD:					  /* word values */
111       if (io_in)
112       {
113 	for (i=0; i<vec_size; i++)
114 	{
115 		port= pvw[i].port;
116 		if (port & 1) goto bad;
117 		pvw[i].value = inw( pvw[i].port);
118 	}
119       }
120       else
121       {
122 	for (i=0; i<vec_size; i++)
123 	{
124 		port= pvw[i].port;
125 		if (port & 1) goto bad;
126 		outw( pvw[i].port, pvw[i].value);
127 	}
128       }
129       break;
130   default:            					  /* long values */
131       if (io_in)
132       {
133 	for (i=0; i<vec_size; i++)
134 	{
135 		port= pvl[i].port;
136 		if (port & 3) goto bad;
137 		pvl[i].value = inl(pvl[i].port);
138 	}
139       }
140       else
141       {
142 	for (i=0; i<vec_size; i++)
143 	{
144 		port= pvl[i].port;
145 		if (port & 3) goto bad;
146 		outl( pvb[i].port, pvl[i].value);
147 	}
148       }
149   }
150 
151   /* Almost done, copy back results for input requests. */
152   if (io_in)
153 	if((r=data_copy(KERNEL, (vir_bytes) vdevio_buf,
154 	  caller->p_endpoint, m_ptr->m_lsys_krn_sys_vdevio.vec_addr,
155 	  (phys_bytes) bytes)) != OK)
156 		return r;
157   return(OK);
158 
159 bad:
160 	panic("do_vdevio: unaligned port: %d", port);
161 	return EPERM;
162 }
163 
164 #endif /* USE_VDEVIO */
165 
166