1 /*	$NetBSD: nouveau_nvif_object.c,v 1.8 2021/12/19 11:07:35 riastradh Exp $	*/
2 
3 /*
4  * Copyright 2014 Red Hat Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * Authors: Ben Skeggs <bskeggs@redhat.com>
25  */
26 
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvif_object.c,v 1.8 2021/12/19 11:07:35 riastradh Exp $");
29 
30 #include <nvif/object.h>
31 #include <nvif/client.h>
32 #include <nvif/driver.h>
33 #include <nvif/ioctl.h>
34 
35 int
nvif_object_ioctl(struct nvif_object * object,void * data,u32 size,void ** hack)36 nvif_object_ioctl(struct nvif_object *object, void *data, u32 size, void **hack)
37 {
38 	struct nvif_client *client = object->client;
39 	union {
40 		struct nvif_ioctl_v0 v0;
41 	} *args = data;
42 
43 	if (size >= sizeof(*args) && args->v0.version == 0) {
44 		if (object != &client->object)
45 			args->v0.object = nvif_handle(object);
46 		else
47 			args->v0.object = 0;
48 		args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY;
49 	} else
50 		return -ENOSYS;
51 
52 	return client->driver->ioctl(client->object.priv, client->super,
53 				     data, size, hack);
54 }
55 
56 void
nvif_object_sclass_put(struct nvif_sclass ** psclass)57 nvif_object_sclass_put(struct nvif_sclass **psclass)
58 {
59 	kfree(*psclass);
60 	*psclass = NULL;
61 }
62 
63 int
nvif_object_sclass_get(struct nvif_object * object,struct nvif_sclass ** psclass)64 nvif_object_sclass_get(struct nvif_object *object, struct nvif_sclass **psclass)
65 {
66 	struct {
67 		struct nvif_ioctl_v0 ioctl;
68 		struct nvif_ioctl_sclass_v0 sclass;
69 	} *args = NULL;
70 	int ret, cnt = 0, i;
71 	u32 size;
72 
73 	while (1) {
74 		size = sizeof(*args) + cnt * sizeof(args->sclass.oclass[0]);
75 		if (!(args = kmalloc(size, GFP_KERNEL)))
76 			return -ENOMEM;
77 		args->ioctl.version = 0;
78 		args->ioctl.type = NVIF_IOCTL_V0_SCLASS;
79 		args->sclass.version = 0;
80 		args->sclass.count = cnt;
81 
82 		ret = nvif_object_ioctl(object, args, size, NULL);
83 		if (ret == 0 && args->sclass.count <= cnt)
84 			break;
85 		cnt = args->sclass.count;
86 		kfree(args);
87 		if (ret != 0)
88 			return ret;
89 	}
90 
91 	*psclass = kcalloc(args->sclass.count, sizeof(**psclass), GFP_KERNEL);
92 	if (*psclass) {
93 		for (i = 0; i < args->sclass.count; i++) {
94 			(*psclass)[i].oclass = args->sclass.oclass[i].oclass;
95 			(*psclass)[i].minver = args->sclass.oclass[i].minver;
96 			(*psclass)[i].maxver = args->sclass.oclass[i].maxver;
97 		}
98 		ret = args->sclass.count;
99 	} else {
100 		ret = -ENOMEM;
101 	}
102 
103 	kfree(args);
104 	return ret;
105 }
106 
107 u32
nvif_object_rd(struct nvif_object * object,int size,u64 addr)108 nvif_object_rd(struct nvif_object *object, int size, u64 addr)
109 {
110 	struct {
111 		struct nvif_ioctl_v0 ioctl;
112 		struct nvif_ioctl_rd_v0 rd;
113 	} args = {
114 		.ioctl.type = NVIF_IOCTL_V0_RD,
115 		.rd.size = size,
116 		.rd.addr = addr,
117 	};
118 	int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
119 	if (ret) {
120 		/*XXX: warn? */
121 		return 0;
122 	}
123 	return args.rd.data;
124 }
125 
126 void
nvif_object_wr(struct nvif_object * object,int size,u64 addr,u32 data)127 nvif_object_wr(struct nvif_object *object, int size, u64 addr, u32 data)
128 {
129 	struct {
130 		struct nvif_ioctl_v0 ioctl;
131 		struct nvif_ioctl_wr_v0 wr;
132 	} args = {
133 		.ioctl.type = NVIF_IOCTL_V0_WR,
134 		.wr.size = size,
135 		.wr.addr = addr,
136 		.wr.data = data,
137 	};
138 	int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
139 	if (ret) {
140 		/*XXX: warn? */
141 	}
142 }
143 
144 int
nvif_object_mthd(struct nvif_object * object,u32 mthd,void * data,u32 size)145 nvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size)
146 {
147 	struct {
148 		struct nvif_ioctl_v0 ioctl;
149 		struct nvif_ioctl_mthd_v0 mthd;
150 	} *args;
151 	u8 stack[128];
152 	int ret;
153 
154 	if (sizeof(*args) + size > sizeof(stack)) {
155 		if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
156 			return -ENOMEM;
157 	} else {
158 		args = (void *)stack;
159 	}
160 	args->ioctl.version = 0;
161 	args->ioctl.type = NVIF_IOCTL_V0_MTHD;
162 	args->mthd.version = 0;
163 	args->mthd.method = mthd;
164 
165 	memcpy(args->mthd.data, data, size);
166 	ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
167 	memcpy(data, args->mthd.data, size);
168 	if (args != (void *)stack)
169 		kfree(args);
170 	return ret;
171 }
172 
173 void
nvif_object_unmap_handle(struct nvif_object * object)174 nvif_object_unmap_handle(struct nvif_object *object)
175 {
176 	struct {
177 		struct nvif_ioctl_v0 ioctl;
178 		struct nvif_ioctl_unmap unmap;
179 	} args = {
180 		.ioctl.type = NVIF_IOCTL_V0_UNMAP,
181 	};
182 
183 	nvif_object_ioctl(object, &args, sizeof(args), NULL);
184 }
185 
186 int
nvif_object_map_handle(struct nvif_object * object,void * argv,u32 argc,bus_space_tag_t * tag,u64 * handle,u64 * length)187 nvif_object_map_handle(struct nvif_object *object, void *argv, u32 argc,
188 #ifdef __NetBSD__
189 		       bus_space_tag_t *tag,
190 #endif
191 		       u64 *handle, u64 *length)
192 {
193 	struct {
194 		struct nvif_ioctl_v0 ioctl;
195 #ifdef __NetBSD__
196 		struct nvif_ioctl_map_netbsd_v0 map;
197 #else
198 		struct nvif_ioctl_map_v0 map;
199 #endif
200 	} *args;
201 	u32 argn = sizeof(*args) + argc;
202 	int ret, maptype;
203 
204 	if (!(args = kzalloc(argn, GFP_KERNEL)))
205 		return -ENOMEM;
206 #ifdef __NetBSD__
207 	args->ioctl.type = NVIF_IOCTL_V0_MAP_NETBSD;
208 #else
209 	args->ioctl.type = NVIF_IOCTL_V0_MAP;
210 #endif
211 	memcpy(args->map.data, argv, argc);
212 
213 	ret = nvif_object_ioctl(object, args, argn, NULL);
214 #ifdef __NetBSD__
215 	if (tag)
216 		*tag = args->map.tag;
217 #endif
218 	*handle = args->map.handle;
219 	*length = args->map.length;
220 	maptype = args->map.type;
221 	kfree(args);
222 	return ret ? ret : (maptype == NVIF_IOCTL_MAP_V0_IO);
223 }
224 
225 void
nvif_object_unmap(struct nvif_object * object)226 nvif_object_unmap(struct nvif_object *object)
227 {
228 	struct nvif_client *client = object->client;
229 	if (object->map.ptr) {
230 		if (object->map.size) {
231 #ifdef __NetBSD__
232 			client->driver->unmap(client, object->map.tag,
233 						      object->map.handle,
234 						      object->map.addr,
235 						      object->map.ptr,
236 						      object->map.size);
237 #else
238 			client->driver->unmap(client, object->map.ptr,
239 						      object->map.size);
240 #endif
241 			object->map.size = 0;
242 		}
243 		object->map.ptr = NULL;
244 		nvif_object_unmap_handle(object);
245 	}
246 }
247 
248 int
nvif_object_map(struct nvif_object * object,void * argv,u32 argc)249 nvif_object_map(struct nvif_object *object, void *argv, u32 argc)
250 {
251 	struct nvif_client *client = object->client;
252 	u64 handle, length;
253 #ifdef __NetBSD__
254 	bus_space_tag_t tag;
255 	int ret = nvif_object_map_handle(object, argv, argc, &tag, &handle, &length);
256 #else
257 	int ret = nvif_object_map_handle(object, argv, argc, &handle, &length);
258 #endif
259 	if (ret >= 0) {
260 		if (ret) {
261 #ifdef __NetBSD__
262 			/*
263 			 * Note: handle is the bus address;
264 			 * object->map.handle is the
265 			 * bus_space_handle_t, which is typically a
266 			 * virtual address mapped in kva.
267 			 */
268 			object->map.tag = tag;
269 			object->map.addr = handle;
270 			ret = client->driver->map(client, tag, handle, length,
271 			    &object->map.handle, &object->map.ptr);
272 			if (ret == 0) {
273 				object->map.size = length;
274 				return 0;
275 			}
276 #else
277 			object->map.ptr = client->driver->map(client,
278 							      handle,
279 							      length);
280 			if (ret = -ENOMEM, object->map.ptr) {
281 				object->map.size = length;
282 				return 0;
283 			}
284 #endif
285 		} else {
286 			object->map.ptr = (void *)(unsigned long)handle;
287 			return 0;
288 		}
289 		nvif_object_unmap_handle(object);
290 	}
291 	return ret;
292 }
293 
294 void
nvif_object_fini(struct nvif_object * object)295 nvif_object_fini(struct nvif_object *object)
296 {
297 	struct {
298 		struct nvif_ioctl_v0 ioctl;
299 		struct nvif_ioctl_del del;
300 	} args = {
301 		.ioctl.type = NVIF_IOCTL_V0_DEL,
302 	};
303 
304 	if (!object->client)
305 		return;
306 
307 	nvif_object_unmap(object);
308 	nvif_object_ioctl(object, &args, sizeof(args), NULL);
309 	object->client = NULL;
310 }
311 
312 int
nvif_object_init(struct nvif_object * parent,u32 handle,s32 oclass,void * data,u32 size,struct nvif_object * object)313 nvif_object_init(struct nvif_object *parent, u32 handle, s32 oclass,
314 		 void *data, u32 size, struct nvif_object *object)
315 {
316 	struct {
317 		struct nvif_ioctl_v0 ioctl;
318 		struct nvif_ioctl_new_v0 new;
319 	} *args;
320 	int ret = 0;
321 
322 	object->client = NULL;
323 	object->handle = handle;
324 	object->oclass = oclass;
325 	object->map.ptr = NULL;
326 	object->map.size = 0;
327 
328 	if (parent) {
329 		if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) {
330 			nvif_object_fini(object);
331 			return -ENOMEM;
332 		}
333 
334 		args->ioctl.version = 0;
335 		args->ioctl.type = NVIF_IOCTL_V0_NEW;
336 		args->new.version = 0;
337 		args->new.route = parent->client->route;
338 		args->new.token = nvif_handle(object);
339 		args->new.object = nvif_handle(object);
340 		args->new.handle = handle;
341 		args->new.oclass = oclass;
342 
343 		memcpy(args->new.data, data, size);
344 		ret = nvif_object_ioctl(parent, args, sizeof(*args) + size,
345 					&object->priv);
346 		memcpy(data, args->new.data, size);
347 		kfree(args);
348 		if (ret == 0)
349 			object->client = parent->client;
350 	}
351 
352 	if (ret)
353 		nvif_object_fini(object);
354 	return ret;
355 }
356