xref: /minix3/minix/kernel/system/do_vumap.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /* The kernel call implemented in this file:
2*433d6423SLionel Sambuc  *   m_type:	SYS_VUMAP
3*433d6423SLionel Sambuc  *
4*433d6423SLionel Sambuc  * The parameters for this kernel call are:
5*433d6423SLionel Sambuc  *   m_lsys_krn_sys_vumap.endpt		(grant owner, or SELF for local addresses)
6*433d6423SLionel Sambuc  *   m_lsys_krn_sys_vumap.vaddr		(address of virtual (input) vector)
7*433d6423SLionel Sambuc  *   m_lsys_krn_sys_vumap.vcount	(number of elements in virtual vector)
8*433d6423SLionel Sambuc  *   m_lsys_krn_sys_vumap.offset	(offset into first entry of input vector)
9*433d6423SLionel Sambuc  *   m_lsys_krn_sys_vumap.access	(safecopy access requested for input)
10*433d6423SLionel Sambuc  *   m_lsys_krn_sys_vumap.paddr		(address of physical (output) vector)
11*433d6423SLionel Sambuc  *   m_lsys_krn_sys_vumap.pmax		(maximum number of physical vector elements)
12*433d6423SLionel Sambuc  *   m_krn_lsys_sys_vumap.pcount	(upon return: number of elements filled)
13*433d6423SLionel Sambuc  */
14*433d6423SLionel Sambuc 
15*433d6423SLionel Sambuc #include "kernel/system.h"
16*433d6423SLionel Sambuc 
17*433d6423SLionel Sambuc #include <assert.h>
18*433d6423SLionel Sambuc 
19*433d6423SLionel Sambuc /*===========================================================================*
20*433d6423SLionel Sambuc  *				do_vumap				     *
21*433d6423SLionel Sambuc  *===========================================================================*/
do_vumap(struct proc * caller,message * m_ptr)22*433d6423SLionel Sambuc int do_vumap(struct proc *caller, message *m_ptr)
23*433d6423SLionel Sambuc {
24*433d6423SLionel Sambuc /* Map a vector of grants or local virtual addresses to physical addresses.
25*433d6423SLionel Sambuc  * Designed to be used by drivers to perform an efficient lookup of physical
26*433d6423SLionel Sambuc  * addresses for the purpose of direct DMA from/to a remote process.
27*433d6423SLionel Sambuc  */
28*433d6423SLionel Sambuc   endpoint_t endpt, source, granter;
29*433d6423SLionel Sambuc   struct proc *procp;
30*433d6423SLionel Sambuc   struct vumap_vir vvec[MAPVEC_NR];
31*433d6423SLionel Sambuc   struct vumap_phys pvec[MAPVEC_NR];
32*433d6423SLionel Sambuc   vir_bytes vaddr, paddr, vir_addr;
33*433d6423SLionel Sambuc   phys_bytes phys_addr;
34*433d6423SLionel Sambuc   int i, r, proc_nr, vcount, pcount, pmax, access;
35*433d6423SLionel Sambuc   size_t size, chunk, offset;
36*433d6423SLionel Sambuc 
37*433d6423SLionel Sambuc   endpt = caller->p_endpoint;
38*433d6423SLionel Sambuc 
39*433d6423SLionel Sambuc   /* Retrieve and check input parameters. */
40*433d6423SLionel Sambuc   source = m_ptr->m_lsys_krn_sys_vumap.endpt;
41*433d6423SLionel Sambuc   vaddr = m_ptr->m_lsys_krn_sys_vumap.vaddr;
42*433d6423SLionel Sambuc   vcount = m_ptr->m_lsys_krn_sys_vumap.vcount;
43*433d6423SLionel Sambuc   offset = m_ptr->m_lsys_krn_sys_vumap.offset;
44*433d6423SLionel Sambuc   access = m_ptr->m_lsys_krn_sys_vumap.access;
45*433d6423SLionel Sambuc   paddr = m_ptr->m_lsys_krn_sys_vumap.paddr;
46*433d6423SLionel Sambuc   pmax = m_ptr->m_lsys_krn_sys_vumap.pmax;
47*433d6423SLionel Sambuc 
48*433d6423SLionel Sambuc   if (vcount <= 0 || pmax <= 0)
49*433d6423SLionel Sambuc 	return EINVAL;
50*433d6423SLionel Sambuc 
51*433d6423SLionel Sambuc   if (vcount > MAPVEC_NR) vcount = MAPVEC_NR;
52*433d6423SLionel Sambuc   if (pmax > MAPVEC_NR) pmax = MAPVEC_NR;
53*433d6423SLionel Sambuc 
54*433d6423SLionel Sambuc   /* Convert access to safecopy access flags. */
55*433d6423SLionel Sambuc   switch (access) {
56*433d6423SLionel Sambuc   case VUA_READ:		access = CPF_READ; break;
57*433d6423SLionel Sambuc   case VUA_WRITE:		access = CPF_WRITE; break;
58*433d6423SLionel Sambuc   case VUA_READ|VUA_WRITE:	access = CPF_READ|CPF_WRITE; break;
59*433d6423SLionel Sambuc   default:			return EINVAL;
60*433d6423SLionel Sambuc   }
61*433d6423SLionel Sambuc 
62*433d6423SLionel Sambuc   /* Copy in the vector of virtual addresses. */
63*433d6423SLionel Sambuc   size = vcount * sizeof(vvec[0]);
64*433d6423SLionel Sambuc 
65*433d6423SLionel Sambuc   if (data_copy(endpt, vaddr, KERNEL, (vir_bytes) vvec, size) != OK)
66*433d6423SLionel Sambuc 	return EFAULT;
67*433d6423SLionel Sambuc 
68*433d6423SLionel Sambuc   pcount = 0;
69*433d6423SLionel Sambuc 
70*433d6423SLionel Sambuc   /* Go through the input entries, one at a time. Stop early in case the output
71*433d6423SLionel Sambuc    * vector has filled up.
72*433d6423SLionel Sambuc    */
73*433d6423SLionel Sambuc   for (i = 0; i < vcount && pcount < pmax; i++) {
74*433d6423SLionel Sambuc 	size = vvec[i].vv_size;
75*433d6423SLionel Sambuc 	if (size <= offset)
76*433d6423SLionel Sambuc 		return EINVAL;
77*433d6423SLionel Sambuc 	size -= offset;
78*433d6423SLionel Sambuc 
79*433d6423SLionel Sambuc 	if (source != SELF) {
80*433d6423SLionel Sambuc 		r = verify_grant(source, endpt, vvec[i].vv_grant, size, access,
81*433d6423SLionel Sambuc 			offset, &vir_addr, &granter, NULL);
82*433d6423SLionel Sambuc 		if (r != OK)
83*433d6423SLionel Sambuc 			return r;
84*433d6423SLionel Sambuc 	} else {
85*433d6423SLionel Sambuc 		vir_addr = vvec[i].vv_addr + offset;
86*433d6423SLionel Sambuc 		granter = endpt;
87*433d6423SLionel Sambuc 	}
88*433d6423SLionel Sambuc 
89*433d6423SLionel Sambuc 	okendpt(granter, &proc_nr);
90*433d6423SLionel Sambuc 	procp = proc_addr(proc_nr);
91*433d6423SLionel Sambuc 
92*433d6423SLionel Sambuc 	/* Each virtual range is made up of one or more physical ranges. */
93*433d6423SLionel Sambuc 	while (size > 0 && pcount < pmax) {
94*433d6423SLionel Sambuc 		chunk = vm_lookup_range(procp, vir_addr, &phys_addr, size);
95*433d6423SLionel Sambuc 
96*433d6423SLionel Sambuc 		if (!chunk) {
97*433d6423SLionel Sambuc 			/* Try to get the memory allocated, unless the memory
98*433d6423SLionel Sambuc 			 * is supposed to be there to be read from.
99*433d6423SLionel Sambuc 			 */
100*433d6423SLionel Sambuc 			if (access & CPF_READ)
101*433d6423SLionel Sambuc 				return EFAULT;
102*433d6423SLionel Sambuc 
103*433d6423SLionel Sambuc 			/* This call may suspend the current call, or return an
104*433d6423SLionel Sambuc 			 * error for a previous invocation.
105*433d6423SLionel Sambuc 			 */
106*433d6423SLionel Sambuc 			return vm_check_range(caller, procp, vir_addr, size, 1);
107*433d6423SLionel Sambuc 		}
108*433d6423SLionel Sambuc 
109*433d6423SLionel Sambuc 		pvec[pcount].vp_addr = phys_addr;
110*433d6423SLionel Sambuc 		pvec[pcount].vp_size = chunk;
111*433d6423SLionel Sambuc 		pcount++;
112*433d6423SLionel Sambuc 
113*433d6423SLionel Sambuc 		vir_addr += chunk;
114*433d6423SLionel Sambuc 		size -= chunk;
115*433d6423SLionel Sambuc 	}
116*433d6423SLionel Sambuc 
117*433d6423SLionel Sambuc 	offset = 0;
118*433d6423SLionel Sambuc   }
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc   /* Copy out the resulting vector of physical addresses. */
121*433d6423SLionel Sambuc   assert(pcount > 0);
122*433d6423SLionel Sambuc 
123*433d6423SLionel Sambuc   size = pcount * sizeof(pvec[0]);
124*433d6423SLionel Sambuc 
125*433d6423SLionel Sambuc   r = data_copy_vmcheck(caller, KERNEL, (vir_bytes) pvec, endpt, paddr, size);
126*433d6423SLionel Sambuc 
127*433d6423SLionel Sambuc   if (r == OK)
128*433d6423SLionel Sambuc 	m_ptr->m_krn_lsys_sys_vumap.pcount = pcount;
129*433d6423SLionel Sambuc 
130*433d6423SLionel Sambuc   return r;
131*433d6423SLionel Sambuc }
132