1920d2b5eSBen Skeggs /*
2920d2b5eSBen Skeggs  * Copyright 2017 Red Hat Inc.
3920d2b5eSBen Skeggs  *
4920d2b5eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5920d2b5eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6920d2b5eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7920d2b5eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8920d2b5eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9920d2b5eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10920d2b5eSBen Skeggs  *
11920d2b5eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12920d2b5eSBen Skeggs  * all copies or substantial portions of the Software.
13920d2b5eSBen Skeggs  *
14920d2b5eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15920d2b5eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16920d2b5eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17920d2b5eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18920d2b5eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19920d2b5eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20920d2b5eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21920d2b5eSBen Skeggs  */
22920d2b5eSBen Skeggs #include "uvmm.h"
23920d2b5eSBen Skeggs #include "umem.h"
24920d2b5eSBen Skeggs #include "ummu.h"
25920d2b5eSBen Skeggs 
26920d2b5eSBen Skeggs #include <core/client.h>
27920d2b5eSBen Skeggs #include <core/memory.h>
28920d2b5eSBen Skeggs 
29920d2b5eSBen Skeggs #include <nvif/if000c.h>
30920d2b5eSBen Skeggs #include <nvif/unpack.h>
31920d2b5eSBen Skeggs 
32920d2b5eSBen Skeggs static const struct nvkm_object_func nvkm_uvmm;
33920d2b5eSBen Skeggs struct nvkm_vmm *
nvkm_uvmm_search(struct nvkm_client * client,u64 handle)34920d2b5eSBen Skeggs nvkm_uvmm_search(struct nvkm_client *client, u64 handle)
35920d2b5eSBen Skeggs {
36920d2b5eSBen Skeggs 	struct nvkm_object *object;
37920d2b5eSBen Skeggs 
38920d2b5eSBen Skeggs 	object = nvkm_object_search(client, handle, &nvkm_uvmm);
39920d2b5eSBen Skeggs 	if (IS_ERR(object))
40920d2b5eSBen Skeggs 		return (void *)object;
41920d2b5eSBen Skeggs 
4206db7fdeSBen Skeggs 	return nvkm_vmm_ref(nvkm_uvmm(object)->vmm);
43920d2b5eSBen Skeggs }
44920d2b5eSBen Skeggs 
45920d2b5eSBen Skeggs static int
nvkm_uvmm_mthd_pfnclr(struct nvkm_uvmm * uvmm,void * argv,u32 argc)46a5ff307fSBen Skeggs nvkm_uvmm_mthd_pfnclr(struct nvkm_uvmm *uvmm, void *argv, u32 argc)
47a5ff307fSBen Skeggs {
48a5ff307fSBen Skeggs 	union {
49a5ff307fSBen Skeggs 		struct nvif_vmm_pfnclr_v0 v0;
50a5ff307fSBen Skeggs 	} *args = argv;
51a5ff307fSBen Skeggs 	struct nvkm_vmm *vmm = uvmm->vmm;
52a5ff307fSBen Skeggs 	int ret = -ENOSYS;
53a5ff307fSBen Skeggs 	u64 addr, size;
54a5ff307fSBen Skeggs 
55a5ff307fSBen Skeggs 	if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) {
56a5ff307fSBen Skeggs 		addr = args->v0.addr;
57a5ff307fSBen Skeggs 		size = args->v0.size;
58a5ff307fSBen Skeggs 	} else
59a5ff307fSBen Skeggs 		return ret;
60a5ff307fSBen Skeggs 
616b252cf4SDanilo Krummrich 	if (nvkm_vmm_in_managed_range(vmm, addr, size) && vmm->managed.raw)
626b252cf4SDanilo Krummrich 		return -EINVAL;
636b252cf4SDanilo Krummrich 
64a5ff307fSBen Skeggs 	if (size) {
656b252cf4SDanilo Krummrich 		mutex_lock(&vmm->mutex.vmm);
66a5ff307fSBen Skeggs 		ret = nvkm_vmm_pfn_unmap(vmm, addr, size);
676b252cf4SDanilo Krummrich 		mutex_unlock(&vmm->mutex.vmm);
68a5ff307fSBen Skeggs 	}
69a5ff307fSBen Skeggs 
70a5ff307fSBen Skeggs 	return ret;
71a5ff307fSBen Skeggs }
72a5ff307fSBen Skeggs 
73a5ff307fSBen Skeggs static int
nvkm_uvmm_mthd_pfnmap(struct nvkm_uvmm * uvmm,void * argv,u32 argc)74a5ff307fSBen Skeggs nvkm_uvmm_mthd_pfnmap(struct nvkm_uvmm *uvmm, void *argv, u32 argc)
75a5ff307fSBen Skeggs {
76a5ff307fSBen Skeggs 	union {
77a5ff307fSBen Skeggs 		struct nvif_vmm_pfnmap_v0 v0;
78a5ff307fSBen Skeggs 	} *args = argv;
79a5ff307fSBen Skeggs 	struct nvkm_vmm *vmm = uvmm->vmm;
80a5ff307fSBen Skeggs 	int ret = -ENOSYS;
81a5ff307fSBen Skeggs 	u64 addr, size, *phys;
82a5ff307fSBen Skeggs 	u8  page;
83a5ff307fSBen Skeggs 
84a5ff307fSBen Skeggs 	if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, true))) {
85a5ff307fSBen Skeggs 		page = args->v0.page;
86a5ff307fSBen Skeggs 		addr = args->v0.addr;
87a5ff307fSBen Skeggs 		size = args->v0.size;
88a5ff307fSBen Skeggs 		phys = args->v0.phys;
89a5ff307fSBen Skeggs 		if (argc != (size >> page) * sizeof(args->v0.phys[0]))
90a5ff307fSBen Skeggs 			return -EINVAL;
91a5ff307fSBen Skeggs 	} else
92a5ff307fSBen Skeggs 		return ret;
93a5ff307fSBen Skeggs 
946b252cf4SDanilo Krummrich 	if (nvkm_vmm_in_managed_range(vmm, addr, size) && vmm->managed.raw)
956b252cf4SDanilo Krummrich 		return -EINVAL;
966b252cf4SDanilo Krummrich 
97a5ff307fSBen Skeggs 	if (size) {
986b252cf4SDanilo Krummrich 		mutex_lock(&vmm->mutex.vmm);
99a5ff307fSBen Skeggs 		ret = nvkm_vmm_pfn_map(vmm, page, addr, size, phys);
1006b252cf4SDanilo Krummrich 		mutex_unlock(&vmm->mutex.vmm);
101a5ff307fSBen Skeggs 	}
102a5ff307fSBen Skeggs 
103a5ff307fSBen Skeggs 	return ret;
104a5ff307fSBen Skeggs }
105a5ff307fSBen Skeggs 
106a5ff307fSBen Skeggs static int
nvkm_uvmm_mthd_unmap(struct nvkm_uvmm * uvmm,void * argv,u32 argc)107920d2b5eSBen Skeggs nvkm_uvmm_mthd_unmap(struct nvkm_uvmm *uvmm, void *argv, u32 argc)
108920d2b5eSBen Skeggs {
109920d2b5eSBen Skeggs 	union {
110920d2b5eSBen Skeggs 		struct nvif_vmm_unmap_v0 v0;
111920d2b5eSBen Skeggs 	} *args = argv;
112920d2b5eSBen Skeggs 	struct nvkm_vmm *vmm = uvmm->vmm;
113920d2b5eSBen Skeggs 	struct nvkm_vma *vma;
114920d2b5eSBen Skeggs 	int ret = -ENOSYS;
115920d2b5eSBen Skeggs 	u64 addr;
116920d2b5eSBen Skeggs 
117920d2b5eSBen Skeggs 	if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) {
118920d2b5eSBen Skeggs 		addr = args->v0.addr;
119920d2b5eSBen Skeggs 	} else
120920d2b5eSBen Skeggs 		return ret;
121920d2b5eSBen Skeggs 
1226b252cf4SDanilo Krummrich 	if (nvkm_vmm_in_managed_range(vmm, addr, 0) && vmm->managed.raw)
1236b252cf4SDanilo Krummrich 		return -EINVAL;
1246b252cf4SDanilo Krummrich 
1256b252cf4SDanilo Krummrich 	mutex_lock(&vmm->mutex.vmm);
126920d2b5eSBen Skeggs 	vma = nvkm_vmm_node_search(vmm, addr);
127920d2b5eSBen Skeggs 	if (ret = -ENOENT, !vma || vma->addr != addr) {
128920d2b5eSBen Skeggs 		VMM_DEBUG(vmm, "lookup %016llx: %016llx",
129920d2b5eSBen Skeggs 			  addr, vma ? vma->addr : ~0ULL);
130920d2b5eSBen Skeggs 		goto done;
131920d2b5eSBen Skeggs 	}
132920d2b5eSBen Skeggs 
13359f216cfSBen Skeggs 	if (ret = -ENOENT, vma->busy) {
13459f216cfSBen Skeggs 		VMM_DEBUG(vmm, "denied %016llx: %d", addr, vma->busy);
135920d2b5eSBen Skeggs 		goto done;
136920d2b5eSBen Skeggs 	}
137920d2b5eSBen Skeggs 
138920d2b5eSBen Skeggs 	if (ret = -EINVAL, !vma->memory) {
139920d2b5eSBen Skeggs 		VMM_DEBUG(vmm, "unmapped");
140920d2b5eSBen Skeggs 		goto done;
141920d2b5eSBen Skeggs 	}
142920d2b5eSBen Skeggs 
143a5ff307fSBen Skeggs 	nvkm_vmm_unmap_locked(vmm, vma, false);
144920d2b5eSBen Skeggs 	ret = 0;
145920d2b5eSBen Skeggs done:
1466b252cf4SDanilo Krummrich 	mutex_unlock(&vmm->mutex.vmm);
147920d2b5eSBen Skeggs 	return ret;
148920d2b5eSBen Skeggs }
149920d2b5eSBen Skeggs 
150920d2b5eSBen Skeggs static int
nvkm_uvmm_mthd_map(struct nvkm_uvmm * uvmm,void * argv,u32 argc)151920d2b5eSBen Skeggs nvkm_uvmm_mthd_map(struct nvkm_uvmm *uvmm, void *argv, u32 argc)
152920d2b5eSBen Skeggs {
153920d2b5eSBen Skeggs 	struct nvkm_client *client = uvmm->object.client;
154920d2b5eSBen Skeggs 	union {
155920d2b5eSBen Skeggs 		struct nvif_vmm_map_v0 v0;
156920d2b5eSBen Skeggs 	} *args = argv;
157920d2b5eSBen Skeggs 	u64 addr, size, handle, offset;
158920d2b5eSBen Skeggs 	struct nvkm_vmm *vmm = uvmm->vmm;
159920d2b5eSBen Skeggs 	struct nvkm_vma *vma;
160920d2b5eSBen Skeggs 	struct nvkm_memory *memory;
161920d2b5eSBen Skeggs 	int ret = -ENOSYS;
162920d2b5eSBen Skeggs 
163920d2b5eSBen Skeggs 	if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, true))) {
164920d2b5eSBen Skeggs 		addr = args->v0.addr;
165920d2b5eSBen Skeggs 		size = args->v0.size;
166920d2b5eSBen Skeggs 		handle = args->v0.memory;
167920d2b5eSBen Skeggs 		offset = args->v0.offset;
168920d2b5eSBen Skeggs 	} else
169920d2b5eSBen Skeggs 		return ret;
170920d2b5eSBen Skeggs 
1716b252cf4SDanilo Krummrich 	if (nvkm_vmm_in_managed_range(vmm, addr, size) && vmm->managed.raw)
1726b252cf4SDanilo Krummrich 		return -EINVAL;
1736b252cf4SDanilo Krummrich 
17494ee54dcSChristoph Böhmwalder 	memory = nvkm_umem_search(client, handle);
17594ee54dcSChristoph Böhmwalder 	if (IS_ERR(memory)) {
176920d2b5eSBen Skeggs 		VMM_DEBUG(vmm, "memory %016llx %ld\n", handle, PTR_ERR(memory));
177920d2b5eSBen Skeggs 		return PTR_ERR(memory);
178920d2b5eSBen Skeggs 	}
179920d2b5eSBen Skeggs 
1806b252cf4SDanilo Krummrich 	mutex_lock(&vmm->mutex.vmm);
181920d2b5eSBen Skeggs 	if (ret = -ENOENT, !(vma = nvkm_vmm_node_search(vmm, addr))) {
182920d2b5eSBen Skeggs 		VMM_DEBUG(vmm, "lookup %016llx", addr);
183920d2b5eSBen Skeggs 		goto fail;
184920d2b5eSBen Skeggs 	}
185920d2b5eSBen Skeggs 
18659f216cfSBen Skeggs 	if (ret = -ENOENT, vma->busy) {
18759f216cfSBen Skeggs 		VMM_DEBUG(vmm, "denied %016llx: %d", addr, vma->busy);
188920d2b5eSBen Skeggs 		goto fail;
189920d2b5eSBen Skeggs 	}
190920d2b5eSBen Skeggs 
191a5ff307fSBen Skeggs 	if (ret = -EINVAL, vma->mapped && !vma->memory) {
192a5ff307fSBen Skeggs 		VMM_DEBUG(vmm, "pfnmap %016llx", addr);
193a5ff307fSBen Skeggs 		goto fail;
194a5ff307fSBen Skeggs 	}
195a5ff307fSBen Skeggs 
196920d2b5eSBen Skeggs 	if (ret = -EINVAL, vma->addr != addr || vma->size != size) {
197920d2b5eSBen Skeggs 		if (addr + size > vma->addr + vma->size || vma->memory ||
198920d2b5eSBen Skeggs 		    (vma->refd == NVKM_VMA_PAGE_NONE && !vma->mapref)) {
199920d2b5eSBen Skeggs 			VMM_DEBUG(vmm, "split %d %d %d "
200920d2b5eSBen Skeggs 				       "%016llx %016llx %016llx %016llx",
201920d2b5eSBen Skeggs 				  !!vma->memory, vma->refd, vma->mapref,
202920d2b5eSBen Skeggs 				  addr, size, vma->addr, (u64)vma->size);
203920d2b5eSBen Skeggs 			goto fail;
204920d2b5eSBen Skeggs 		}
205920d2b5eSBen Skeggs 
206729eba33SBen Skeggs 		vma = nvkm_vmm_node_split(vmm, vma, addr, size);
207729eba33SBen Skeggs 		if (!vma) {
208729eba33SBen Skeggs 			ret = -ENOMEM;
209920d2b5eSBen Skeggs 			goto fail;
210920d2b5eSBen Skeggs 		}
211920d2b5eSBen Skeggs 	}
212920d2b5eSBen Skeggs 	vma->busy = true;
2136b252cf4SDanilo Krummrich 	mutex_unlock(&vmm->mutex.vmm);
214920d2b5eSBen Skeggs 
215920d2b5eSBen Skeggs 	ret = nvkm_memory_map(memory, offset, vmm, vma, argv, argc);
216920d2b5eSBen Skeggs 	if (ret == 0) {
217920d2b5eSBen Skeggs 		/* Successful map will clear vma->busy. */
218920d2b5eSBen Skeggs 		nvkm_memory_unref(&memory);
219920d2b5eSBen Skeggs 		return 0;
220920d2b5eSBen Skeggs 	}
221920d2b5eSBen Skeggs 
2226b252cf4SDanilo Krummrich 	mutex_lock(&vmm->mutex.vmm);
223920d2b5eSBen Skeggs 	vma->busy = false;
224920d2b5eSBen Skeggs 	nvkm_vmm_unmap_region(vmm, vma);
225920d2b5eSBen Skeggs fail:
2266b252cf4SDanilo Krummrich 	mutex_unlock(&vmm->mutex.vmm);
227920d2b5eSBen Skeggs 	nvkm_memory_unref(&memory);
228920d2b5eSBen Skeggs 	return ret;
229920d2b5eSBen Skeggs }
230920d2b5eSBen Skeggs 
231920d2b5eSBen Skeggs static int
nvkm_uvmm_mthd_put(struct nvkm_uvmm * uvmm,void * argv,u32 argc)232920d2b5eSBen Skeggs nvkm_uvmm_mthd_put(struct nvkm_uvmm *uvmm, void *argv, u32 argc)
233920d2b5eSBen Skeggs {
234920d2b5eSBen Skeggs 	union {
235920d2b5eSBen Skeggs 		struct nvif_vmm_put_v0 v0;
236920d2b5eSBen Skeggs 	} *args = argv;
237920d2b5eSBen Skeggs 	struct nvkm_vmm *vmm = uvmm->vmm;
238920d2b5eSBen Skeggs 	struct nvkm_vma *vma;
239920d2b5eSBen Skeggs 	int ret = -ENOSYS;
240920d2b5eSBen Skeggs 	u64 addr;
241920d2b5eSBen Skeggs 
242920d2b5eSBen Skeggs 	if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) {
243920d2b5eSBen Skeggs 		addr = args->v0.addr;
244920d2b5eSBen Skeggs 	} else
245920d2b5eSBen Skeggs 		return ret;
246920d2b5eSBen Skeggs 
2476b252cf4SDanilo Krummrich 	mutex_lock(&vmm->mutex.vmm);
248920d2b5eSBen Skeggs 	vma = nvkm_vmm_node_search(vmm, args->v0.addr);
249920d2b5eSBen Skeggs 	if (ret = -ENOENT, !vma || vma->addr != addr || vma->part) {
250920d2b5eSBen Skeggs 		VMM_DEBUG(vmm, "lookup %016llx: %016llx %d", addr,
251920d2b5eSBen Skeggs 			  vma ? vma->addr : ~0ULL, vma ? vma->part : 0);
252920d2b5eSBen Skeggs 		goto done;
253920d2b5eSBen Skeggs 	}
254920d2b5eSBen Skeggs 
25559f216cfSBen Skeggs 	if (ret = -ENOENT, vma->busy) {
25659f216cfSBen Skeggs 		VMM_DEBUG(vmm, "denied %016llx: %d", addr, vma->busy);
257920d2b5eSBen Skeggs 		goto done;
258920d2b5eSBen Skeggs 	}
259920d2b5eSBen Skeggs 
260920d2b5eSBen Skeggs 	nvkm_vmm_put_locked(vmm, vma);
261920d2b5eSBen Skeggs 	ret = 0;
262920d2b5eSBen Skeggs done:
2636b252cf4SDanilo Krummrich 	mutex_unlock(&vmm->mutex.vmm);
264920d2b5eSBen Skeggs 	return ret;
265920d2b5eSBen Skeggs }
266920d2b5eSBen Skeggs 
267920d2b5eSBen Skeggs static int
nvkm_uvmm_mthd_get(struct nvkm_uvmm * uvmm,void * argv,u32 argc)268920d2b5eSBen Skeggs nvkm_uvmm_mthd_get(struct nvkm_uvmm *uvmm, void *argv, u32 argc)
269920d2b5eSBen Skeggs {
270920d2b5eSBen Skeggs 	union {
271920d2b5eSBen Skeggs 		struct nvif_vmm_get_v0 v0;
272920d2b5eSBen Skeggs 	} *args = argv;
273920d2b5eSBen Skeggs 	struct nvkm_vmm *vmm = uvmm->vmm;
274920d2b5eSBen Skeggs 	struct nvkm_vma *vma;
275920d2b5eSBen Skeggs 	int ret = -ENOSYS;
276920d2b5eSBen Skeggs 	bool getref, mapref, sparse;
277920d2b5eSBen Skeggs 	u8 page, align;
278920d2b5eSBen Skeggs 	u64 size;
279920d2b5eSBen Skeggs 
280920d2b5eSBen Skeggs 	if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) {
281920d2b5eSBen Skeggs 		getref = args->v0.type == NVIF_VMM_GET_V0_PTES;
282920d2b5eSBen Skeggs 		mapref = args->v0.type == NVIF_VMM_GET_V0_ADDR;
283920d2b5eSBen Skeggs 		sparse = args->v0.sparse;
284920d2b5eSBen Skeggs 		page = args->v0.page;
285920d2b5eSBen Skeggs 		align = args->v0.align;
286920d2b5eSBen Skeggs 		size = args->v0.size;
287920d2b5eSBen Skeggs 	} else
288920d2b5eSBen Skeggs 		return ret;
289920d2b5eSBen Skeggs 
2906b252cf4SDanilo Krummrich 	mutex_lock(&vmm->mutex.vmm);
291920d2b5eSBen Skeggs 	ret = nvkm_vmm_get_locked(vmm, getref, mapref, sparse,
292920d2b5eSBen Skeggs 				  page, align, size, &vma);
2936b252cf4SDanilo Krummrich 	mutex_unlock(&vmm->mutex.vmm);
294920d2b5eSBen Skeggs 	if (ret)
295920d2b5eSBen Skeggs 		return ret;
296920d2b5eSBen Skeggs 
297920d2b5eSBen Skeggs 	args->v0.addr = vma->addr;
298920d2b5eSBen Skeggs 	return ret;
299920d2b5eSBen Skeggs }
300920d2b5eSBen Skeggs 
301920d2b5eSBen Skeggs static int
nvkm_uvmm_mthd_page(struct nvkm_uvmm * uvmm,void * argv,u32 argc)302920d2b5eSBen Skeggs nvkm_uvmm_mthd_page(struct nvkm_uvmm *uvmm, void *argv, u32 argc)
303920d2b5eSBen Skeggs {
304920d2b5eSBen Skeggs 	union {
305920d2b5eSBen Skeggs 		struct nvif_vmm_page_v0 v0;
306920d2b5eSBen Skeggs 	} *args = argv;
307920d2b5eSBen Skeggs 	const struct nvkm_vmm_page *page;
308920d2b5eSBen Skeggs 	int ret = -ENOSYS;
309920d2b5eSBen Skeggs 	u8 type, index, nr;
310920d2b5eSBen Skeggs 
311920d2b5eSBen Skeggs 	page = uvmm->vmm->func->page;
312920d2b5eSBen Skeggs 	for (nr = 0; page[nr].shift; nr++);
313920d2b5eSBen Skeggs 
31463631859SLuo penghao 	if (!(nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))) {
315920d2b5eSBen Skeggs 		if ((index = args->v0.index) >= nr)
316920d2b5eSBen Skeggs 			return -EINVAL;
317920d2b5eSBen Skeggs 		type = page[index].type;
318920d2b5eSBen Skeggs 		args->v0.shift = page[index].shift;
319920d2b5eSBen Skeggs 		args->v0.sparse = !!(type & NVKM_VMM_PAGE_SPARSE);
320920d2b5eSBen Skeggs 		args->v0.vram = !!(type & NVKM_VMM_PAGE_VRAM);
321920d2b5eSBen Skeggs 		args->v0.host = !!(type & NVKM_VMM_PAGE_HOST);
322920d2b5eSBen Skeggs 		args->v0.comp = !!(type & NVKM_VMM_PAGE_COMP);
323920d2b5eSBen Skeggs 	} else
324920d2b5eSBen Skeggs 		return -ENOSYS;
325920d2b5eSBen Skeggs 
326920d2b5eSBen Skeggs 	return 0;
327920d2b5eSBen Skeggs }
328920d2b5eSBen Skeggs 
3296b252cf4SDanilo Krummrich static inline int
nvkm_uvmm_page_index(struct nvkm_uvmm * uvmm,u64 size,u8 shift,u8 * refd)3306b252cf4SDanilo Krummrich nvkm_uvmm_page_index(struct nvkm_uvmm *uvmm, u64 size, u8 shift, u8 *refd)
3316b252cf4SDanilo Krummrich {
3326b252cf4SDanilo Krummrich 	struct nvkm_vmm *vmm = uvmm->vmm;
3336b252cf4SDanilo Krummrich 	const struct nvkm_vmm_page *page;
3346b252cf4SDanilo Krummrich 
3356b252cf4SDanilo Krummrich 	if (likely(shift)) {
3366b252cf4SDanilo Krummrich 		for (page = vmm->func->page; page->shift; page++) {
3376b252cf4SDanilo Krummrich 			if (shift == page->shift)
3386b252cf4SDanilo Krummrich 				break;
3396b252cf4SDanilo Krummrich 		}
3406b252cf4SDanilo Krummrich 
3416b252cf4SDanilo Krummrich 		if (!page->shift || !IS_ALIGNED(size, 1ULL << page->shift)) {
3426b252cf4SDanilo Krummrich 			VMM_DEBUG(vmm, "page %d %016llx", shift, size);
3436b252cf4SDanilo Krummrich 			return -EINVAL;
3446b252cf4SDanilo Krummrich 		}
3456b252cf4SDanilo Krummrich 	} else {
3466b252cf4SDanilo Krummrich 		return -EINVAL;
3476b252cf4SDanilo Krummrich 	}
3486b252cf4SDanilo Krummrich 	*refd = page - vmm->func->page;
3496b252cf4SDanilo Krummrich 
3506b252cf4SDanilo Krummrich 	return 0;
3516b252cf4SDanilo Krummrich }
3526b252cf4SDanilo Krummrich 
3536b252cf4SDanilo Krummrich static int
nvkm_uvmm_mthd_raw_get(struct nvkm_uvmm * uvmm,struct nvif_vmm_raw_v0 * args)3546b252cf4SDanilo Krummrich nvkm_uvmm_mthd_raw_get(struct nvkm_uvmm *uvmm, struct nvif_vmm_raw_v0 *args)
3556b252cf4SDanilo Krummrich {
3566b252cf4SDanilo Krummrich 	struct nvkm_vmm *vmm = uvmm->vmm;
3576b252cf4SDanilo Krummrich 	u8 refd;
3586b252cf4SDanilo Krummrich 	int ret;
3596b252cf4SDanilo Krummrich 
3606b252cf4SDanilo Krummrich 	if (!nvkm_vmm_in_managed_range(vmm, args->addr, args->size))
3616b252cf4SDanilo Krummrich 		return -EINVAL;
3626b252cf4SDanilo Krummrich 
3636b252cf4SDanilo Krummrich 	ret = nvkm_uvmm_page_index(uvmm, args->size, args->shift, &refd);
3646b252cf4SDanilo Krummrich 	if (ret)
3656b252cf4SDanilo Krummrich 		return ret;
3666b252cf4SDanilo Krummrich 
3676b252cf4SDanilo Krummrich 	return nvkm_vmm_raw_get(vmm, args->addr, args->size, refd);
3686b252cf4SDanilo Krummrich }
3696b252cf4SDanilo Krummrich 
3706b252cf4SDanilo Krummrich static int
nvkm_uvmm_mthd_raw_put(struct nvkm_uvmm * uvmm,struct nvif_vmm_raw_v0 * args)3716b252cf4SDanilo Krummrich nvkm_uvmm_mthd_raw_put(struct nvkm_uvmm *uvmm, struct nvif_vmm_raw_v0 *args)
3726b252cf4SDanilo Krummrich {
3736b252cf4SDanilo Krummrich 	struct nvkm_vmm *vmm = uvmm->vmm;
3746b252cf4SDanilo Krummrich 	u8 refd;
3756b252cf4SDanilo Krummrich 	int ret;
3766b252cf4SDanilo Krummrich 
3776b252cf4SDanilo Krummrich 	if (!nvkm_vmm_in_managed_range(vmm, args->addr, args->size))
3786b252cf4SDanilo Krummrich 		return -EINVAL;
3796b252cf4SDanilo Krummrich 
3806b252cf4SDanilo Krummrich 	ret = nvkm_uvmm_page_index(uvmm, args->size, args->shift, &refd);
3816b252cf4SDanilo Krummrich 	if (ret)
3826b252cf4SDanilo Krummrich 		return ret;
3836b252cf4SDanilo Krummrich 
3846b252cf4SDanilo Krummrich 	nvkm_vmm_raw_put(vmm, args->addr, args->size, refd);
3856b252cf4SDanilo Krummrich 
3866b252cf4SDanilo Krummrich 	return 0;
3876b252cf4SDanilo Krummrich }
3886b252cf4SDanilo Krummrich 
3896b252cf4SDanilo Krummrich static int
nvkm_uvmm_mthd_raw_map(struct nvkm_uvmm * uvmm,struct nvif_vmm_raw_v0 * args)3906b252cf4SDanilo Krummrich nvkm_uvmm_mthd_raw_map(struct nvkm_uvmm *uvmm, struct nvif_vmm_raw_v0 *args)
3916b252cf4SDanilo Krummrich {
3926b252cf4SDanilo Krummrich 	struct nvkm_client *client = uvmm->object.client;
3936b252cf4SDanilo Krummrich 	struct nvkm_vmm *vmm = uvmm->vmm;
3946b252cf4SDanilo Krummrich 	struct nvkm_vma vma = {
3956b252cf4SDanilo Krummrich 		.addr = args->addr,
3966b252cf4SDanilo Krummrich 		.size = args->size,
3976b252cf4SDanilo Krummrich 		.used = true,
3986b252cf4SDanilo Krummrich 		.mapref = false,
3996b252cf4SDanilo Krummrich 		.no_comp = true,
4006b252cf4SDanilo Krummrich 	};
4016b252cf4SDanilo Krummrich 	struct nvkm_memory *memory;
402*85b7d20fSDanilo Krummrich 	void *argv = (void *)(uintptr_t)args->argv;
403*85b7d20fSDanilo Krummrich 	unsigned int argc = args->argc;
4046b252cf4SDanilo Krummrich 	u64 handle = args->memory;
4056b252cf4SDanilo Krummrich 	u8 refd;
4066b252cf4SDanilo Krummrich 	int ret;
4076b252cf4SDanilo Krummrich 
4086b252cf4SDanilo Krummrich 	if (!nvkm_vmm_in_managed_range(vmm, args->addr, args->size))
4096b252cf4SDanilo Krummrich 		return -EINVAL;
4106b252cf4SDanilo Krummrich 
4116b252cf4SDanilo Krummrich 	ret = nvkm_uvmm_page_index(uvmm, args->size, args->shift, &refd);
4126b252cf4SDanilo Krummrich 	if (ret)
4136b252cf4SDanilo Krummrich 		return ret;
4146b252cf4SDanilo Krummrich 
4156b252cf4SDanilo Krummrich 	vma.page = vma.refd = refd;
4166b252cf4SDanilo Krummrich 
4176b252cf4SDanilo Krummrich 	memory = nvkm_umem_search(client, args->memory);
4186b252cf4SDanilo Krummrich 	if (IS_ERR(memory)) {
4196b252cf4SDanilo Krummrich 		VMM_DEBUG(vmm, "memory %016llx %ld\n", handle, PTR_ERR(memory));
4206b252cf4SDanilo Krummrich 		return PTR_ERR(memory);
4216b252cf4SDanilo Krummrich 	}
4226b252cf4SDanilo Krummrich 
423*85b7d20fSDanilo Krummrich 	ret = nvkm_memory_map(memory, args->offset, vmm, &vma, argv, argc);
4246b252cf4SDanilo Krummrich 
4256b252cf4SDanilo Krummrich 	nvkm_memory_unref(&vma.memory);
4266b252cf4SDanilo Krummrich 	nvkm_memory_unref(&memory);
4276b252cf4SDanilo Krummrich 	return ret;
4286b252cf4SDanilo Krummrich }
4296b252cf4SDanilo Krummrich 
4306b252cf4SDanilo Krummrich static int
nvkm_uvmm_mthd_raw_unmap(struct nvkm_uvmm * uvmm,struct nvif_vmm_raw_v0 * args)4316b252cf4SDanilo Krummrich nvkm_uvmm_mthd_raw_unmap(struct nvkm_uvmm *uvmm, struct nvif_vmm_raw_v0 *args)
4326b252cf4SDanilo Krummrich {
4336b252cf4SDanilo Krummrich 	struct nvkm_vmm *vmm = uvmm->vmm;
4346b252cf4SDanilo Krummrich 	u8 refd;
4356b252cf4SDanilo Krummrich 	int ret;
4366b252cf4SDanilo Krummrich 
4376b252cf4SDanilo Krummrich 	if (!nvkm_vmm_in_managed_range(vmm, args->addr, args->size))
4386b252cf4SDanilo Krummrich 		return -EINVAL;
4396b252cf4SDanilo Krummrich 
4406b252cf4SDanilo Krummrich 	ret = nvkm_uvmm_page_index(uvmm, args->size, args->shift, &refd);
4416b252cf4SDanilo Krummrich 	if (ret)
4426b252cf4SDanilo Krummrich 		return ret;
4436b252cf4SDanilo Krummrich 
4446b252cf4SDanilo Krummrich 	nvkm_vmm_raw_unmap(vmm, args->addr, args->size,
4456b252cf4SDanilo Krummrich 			   args->sparse, refd);
4466b252cf4SDanilo Krummrich 
4476b252cf4SDanilo Krummrich 	return 0;
4486b252cf4SDanilo Krummrich }
4496b252cf4SDanilo Krummrich 
4506b252cf4SDanilo Krummrich static int
nvkm_uvmm_mthd_raw_sparse(struct nvkm_uvmm * uvmm,struct nvif_vmm_raw_v0 * args)4516b252cf4SDanilo Krummrich nvkm_uvmm_mthd_raw_sparse(struct nvkm_uvmm *uvmm, struct nvif_vmm_raw_v0 *args)
4526b252cf4SDanilo Krummrich {
4536b252cf4SDanilo Krummrich 	struct nvkm_vmm *vmm = uvmm->vmm;
4546b252cf4SDanilo Krummrich 
4556b252cf4SDanilo Krummrich 	if (!nvkm_vmm_in_managed_range(vmm, args->addr, args->size))
4566b252cf4SDanilo Krummrich 		return -EINVAL;
4576b252cf4SDanilo Krummrich 
4586b252cf4SDanilo Krummrich 	return nvkm_vmm_raw_sparse(vmm, args->addr, args->size, args->ref);
4596b252cf4SDanilo Krummrich }
4606b252cf4SDanilo Krummrich 
4616b252cf4SDanilo Krummrich static int
nvkm_uvmm_mthd_raw(struct nvkm_uvmm * uvmm,void * argv,u32 argc)4626b252cf4SDanilo Krummrich nvkm_uvmm_mthd_raw(struct nvkm_uvmm *uvmm, void *argv, u32 argc)
4636b252cf4SDanilo Krummrich {
4646b252cf4SDanilo Krummrich 	union {
4656b252cf4SDanilo Krummrich 		struct nvif_vmm_raw_v0 v0;
4666b252cf4SDanilo Krummrich 	} *args = argv;
4676b252cf4SDanilo Krummrich 	int ret = -ENOSYS;
4686b252cf4SDanilo Krummrich 
4696b252cf4SDanilo Krummrich 	if (!uvmm->vmm->managed.raw)
4706b252cf4SDanilo Krummrich 		return -EINVAL;
4716b252cf4SDanilo Krummrich 
4726b252cf4SDanilo Krummrich 	if ((ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, true)))
4736b252cf4SDanilo Krummrich 		return ret;
4746b252cf4SDanilo Krummrich 
4756b252cf4SDanilo Krummrich 	switch (args->v0.op) {
4766b252cf4SDanilo Krummrich 	case NVIF_VMM_RAW_V0_GET:
4776b252cf4SDanilo Krummrich 		return nvkm_uvmm_mthd_raw_get(uvmm, &args->v0);
4786b252cf4SDanilo Krummrich 	case NVIF_VMM_RAW_V0_PUT:
4796b252cf4SDanilo Krummrich 		return nvkm_uvmm_mthd_raw_put(uvmm, &args->v0);
4806b252cf4SDanilo Krummrich 	case NVIF_VMM_RAW_V0_MAP:
4816b252cf4SDanilo Krummrich 		return nvkm_uvmm_mthd_raw_map(uvmm, &args->v0);
4826b252cf4SDanilo Krummrich 	case NVIF_VMM_RAW_V0_UNMAP:
4836b252cf4SDanilo Krummrich 		return nvkm_uvmm_mthd_raw_unmap(uvmm, &args->v0);
4846b252cf4SDanilo Krummrich 	case NVIF_VMM_RAW_V0_SPARSE:
4856b252cf4SDanilo Krummrich 		return nvkm_uvmm_mthd_raw_sparse(uvmm, &args->v0);
4866b252cf4SDanilo Krummrich 	default:
4876b252cf4SDanilo Krummrich 		return -EINVAL;
4886b252cf4SDanilo Krummrich 	};
4896b252cf4SDanilo Krummrich }
4906b252cf4SDanilo Krummrich 
491920d2b5eSBen Skeggs static int
nvkm_uvmm_mthd(struct nvkm_object * object,u32 mthd,void * argv,u32 argc)492920d2b5eSBen Skeggs nvkm_uvmm_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)
493920d2b5eSBen Skeggs {
494920d2b5eSBen Skeggs 	struct nvkm_uvmm *uvmm = nvkm_uvmm(object);
495920d2b5eSBen Skeggs 	switch (mthd) {
496920d2b5eSBen Skeggs 	case NVIF_VMM_V0_PAGE  : return nvkm_uvmm_mthd_page  (uvmm, argv, argc);
497920d2b5eSBen Skeggs 	case NVIF_VMM_V0_GET   : return nvkm_uvmm_mthd_get   (uvmm, argv, argc);
498920d2b5eSBen Skeggs 	case NVIF_VMM_V0_PUT   : return nvkm_uvmm_mthd_put   (uvmm, argv, argc);
499920d2b5eSBen Skeggs 	case NVIF_VMM_V0_MAP   : return nvkm_uvmm_mthd_map   (uvmm, argv, argc);
500920d2b5eSBen Skeggs 	case NVIF_VMM_V0_UNMAP : return nvkm_uvmm_mthd_unmap (uvmm, argv, argc);
501a5ff307fSBen Skeggs 	case NVIF_VMM_V0_PFNMAP: return nvkm_uvmm_mthd_pfnmap(uvmm, argv, argc);
502a5ff307fSBen Skeggs 	case NVIF_VMM_V0_PFNCLR: return nvkm_uvmm_mthd_pfnclr(uvmm, argv, argc);
5036b252cf4SDanilo Krummrich 	case NVIF_VMM_V0_RAW   : return nvkm_uvmm_mthd_raw   (uvmm, argv, argc);
50471871aa6SBen Skeggs 	case NVIF_VMM_V0_MTHD(0x00) ... NVIF_VMM_V0_MTHD(0x7f):
50571871aa6SBen Skeggs 		if (uvmm->vmm->func->mthd) {
50671871aa6SBen Skeggs 			return uvmm->vmm->func->mthd(uvmm->vmm,
50771871aa6SBen Skeggs 						     uvmm->object.client,
50871871aa6SBen Skeggs 						     mthd, argv, argc);
50971871aa6SBen Skeggs 		}
51071871aa6SBen Skeggs 		break;
511920d2b5eSBen Skeggs 	default:
512920d2b5eSBen Skeggs 		break;
513920d2b5eSBen Skeggs 	}
514920d2b5eSBen Skeggs 	return -EINVAL;
515920d2b5eSBen Skeggs }
516920d2b5eSBen Skeggs 
517920d2b5eSBen Skeggs static void *
nvkm_uvmm_dtor(struct nvkm_object * object)518920d2b5eSBen Skeggs nvkm_uvmm_dtor(struct nvkm_object *object)
519920d2b5eSBen Skeggs {
520920d2b5eSBen Skeggs 	struct nvkm_uvmm *uvmm = nvkm_uvmm(object);
521920d2b5eSBen Skeggs 	nvkm_vmm_unref(&uvmm->vmm);
522920d2b5eSBen Skeggs 	return uvmm;
523920d2b5eSBen Skeggs }
524920d2b5eSBen Skeggs 
525920d2b5eSBen Skeggs static const struct nvkm_object_func
526920d2b5eSBen Skeggs nvkm_uvmm = {
527920d2b5eSBen Skeggs 	.dtor = nvkm_uvmm_dtor,
528920d2b5eSBen Skeggs 	.mthd = nvkm_uvmm_mthd,
529920d2b5eSBen Skeggs };
530920d2b5eSBen Skeggs 
531920d2b5eSBen Skeggs int
nvkm_uvmm_new(const struct nvkm_oclass * oclass,void * argv,u32 argc,struct nvkm_object ** pobject)532920d2b5eSBen Skeggs nvkm_uvmm_new(const struct nvkm_oclass *oclass, void *argv, u32 argc,
533920d2b5eSBen Skeggs 	      struct nvkm_object **pobject)
534920d2b5eSBen Skeggs {
535920d2b5eSBen Skeggs 	struct nvkm_mmu *mmu = nvkm_ummu(oclass->parent)->mmu;
536920d2b5eSBen Skeggs 	const bool more = oclass->base.maxver >= 0;
537920d2b5eSBen Skeggs 	union {
538920d2b5eSBen Skeggs 		struct nvif_vmm_v0 v0;
539920d2b5eSBen Skeggs 	} *args = argv;
540920d2b5eSBen Skeggs 	const struct nvkm_vmm_page *page;
541920d2b5eSBen Skeggs 	struct nvkm_uvmm *uvmm;
542920d2b5eSBen Skeggs 	int ret = -ENOSYS;
543920d2b5eSBen Skeggs 	u64 addr, size;
5446b252cf4SDanilo Krummrich 	bool managed, raw;
545920d2b5eSBen Skeggs 
546920d2b5eSBen Skeggs 	if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, more))) {
5476b252cf4SDanilo Krummrich 		managed = args->v0.type == NVIF_VMM_V0_TYPE_MANAGED;
5486b252cf4SDanilo Krummrich 		raw = args->v0.type == NVIF_VMM_V0_TYPE_RAW;
549920d2b5eSBen Skeggs 		addr = args->v0.addr;
550920d2b5eSBen Skeggs 		size = args->v0.size;
551920d2b5eSBen Skeggs 	} else
552920d2b5eSBen Skeggs 		return ret;
553920d2b5eSBen Skeggs 
554920d2b5eSBen Skeggs 	if (!(uvmm = kzalloc(sizeof(*uvmm), GFP_KERNEL)))
555920d2b5eSBen Skeggs 		return -ENOMEM;
5566b252cf4SDanilo Krummrich 
557920d2b5eSBen Skeggs 	nvkm_object_ctor(&nvkm_uvmm, oclass, &uvmm->object);
558920d2b5eSBen Skeggs 	*pobject = &uvmm->object;
559920d2b5eSBen Skeggs 
560920d2b5eSBen Skeggs 	if (!mmu->vmm) {
5616b252cf4SDanilo Krummrich 		ret = mmu->func->vmm.ctor(mmu, managed || raw, addr, size,
5626b252cf4SDanilo Krummrich 					  argv, argc, NULL, "user", &uvmm->vmm);
563920d2b5eSBen Skeggs 		if (ret)
564920d2b5eSBen Skeggs 			return ret;
565920d2b5eSBen Skeggs 
566920d2b5eSBen Skeggs 		uvmm->vmm->debug = max(uvmm->vmm->debug, oclass->client->debug);
567920d2b5eSBen Skeggs 	} else {
568920d2b5eSBen Skeggs 		if (size)
569920d2b5eSBen Skeggs 			return -EINVAL;
570920d2b5eSBen Skeggs 
571920d2b5eSBen Skeggs 		uvmm->vmm = nvkm_vmm_ref(mmu->vmm);
572920d2b5eSBen Skeggs 	}
5736b252cf4SDanilo Krummrich 	uvmm->vmm->managed.raw = raw;
574920d2b5eSBen Skeggs 
575920d2b5eSBen Skeggs 	page = uvmm->vmm->func->page;
576920d2b5eSBen Skeggs 	args->v0.page_nr = 0;
577920d2b5eSBen Skeggs 	while (page && (page++)->shift)
578920d2b5eSBen Skeggs 		args->v0.page_nr++;
579920d2b5eSBen Skeggs 	args->v0.addr = uvmm->vmm->start;
580920d2b5eSBen Skeggs 	args->v0.size = uvmm->vmm->limit;
581920d2b5eSBen Skeggs 	return 0;
582920d2b5eSBen Skeggs }
583