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