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