Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/drm-shim/device.c
4558 views
1
/*
2
* Copyright © 2018 Broadcom
3
*
4
* Permission is hereby granted, free of charge, to any person obtaining a
5
* copy of this software and associated documentation files (the "Software"),
6
* to deal in the Software without restriction, including without limitation
7
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
* and/or sell copies of the Software, and to permit persons to whom the
9
* Software is furnished to do so, subject to the following conditions:
10
*
11
* The above copyright notice and this permission notice (including the next
12
* paragraph) shall be included in all copies or substantial portions of the
13
* Software.
14
*
15
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
* DEALINGS IN THE SOFTWARE.
22
*/
23
24
/** @file
25
*
26
* Implements core GEM support (particularly ioctls) underneath the libc ioctl
27
* wrappers, and calls into the driver-specific code as necessary.
28
*/
29
30
#include <c11/threads.h>
31
#include <errno.h>
32
#include <linux/memfd.h>
33
#include <stdbool.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <sys/ioctl.h>
38
#include <sys/mman.h>
39
#include <unistd.h>
40
#include "drm-uapi/drm.h"
41
#include "drm_shim.h"
42
#include "util/hash_table.h"
43
#include "util/u_atomic.h"
44
45
#define SHIM_MEM_SIZE (4ull * 1024 * 1024 * 1024)
46
47
#ifndef HAVE_MEMFD_CREATE
48
#include <sys/syscall.h>
49
50
static inline int
51
memfd_create(const char *name, unsigned int flags)
52
{
53
return syscall(SYS_memfd_create, name, flags);
54
}
55
#endif
56
57
/* Global state for the shim shared between libc, core, and driver. */
58
struct shim_device shim_device;
59
60
long shim_page_size;
61
62
static uint32_t
63
uint_key_hash(const void *key)
64
{
65
return (uintptr_t)key;
66
}
67
68
static bool
69
uint_key_compare(const void *a, const void *b)
70
{
71
return a == b;
72
}
73
74
/**
75
* Called when the first libc shim is called, to initialize GEM simulation
76
* state (other than the shims themselves).
77
*/
78
void
79
drm_shim_device_init(void)
80
{
81
shim_device.fd_map = _mesa_hash_table_create(NULL,
82
uint_key_hash,
83
uint_key_compare);
84
85
mtx_init(&shim_device.mem_lock, mtx_plain);
86
87
shim_device.mem_fd = memfd_create("shim mem", MFD_CLOEXEC);
88
assert(shim_device.mem_fd != -1);
89
90
ASSERTED int ret = ftruncate(shim_device.mem_fd, SHIM_MEM_SIZE);
91
assert(ret == 0);
92
93
/* The man page for mmap() says
94
*
95
* offset must be a multiple of the page size as returned by
96
* sysconf(_SC_PAGE_SIZE).
97
*
98
* Depending on the configuration of the kernel, this may not be 4096. Get
99
* this page size once and use it as the page size throughout, ensuring that
100
* are offsets are page-size aligned as required. Otherwise, mmap will fail
101
* with EINVAL.
102
*/
103
104
shim_page_size = sysconf(_SC_PAGE_SIZE);
105
106
util_vma_heap_init(&shim_device.mem_heap, shim_page_size,
107
SHIM_MEM_SIZE - shim_page_size);
108
109
drm_shim_driver_init();
110
}
111
112
static struct shim_fd *
113
drm_shim_file_create(int fd)
114
{
115
struct shim_fd *shim_fd = calloc(1, sizeof(*shim_fd));
116
117
shim_fd->fd = fd;
118
mtx_init(&shim_fd->handle_lock, mtx_plain);
119
shim_fd->handles = _mesa_hash_table_create(NULL,
120
uint_key_hash,
121
uint_key_compare);
122
123
return shim_fd;
124
}
125
126
/**
127
* Called when the libc shims have interposed an open or dup of our simulated
128
* DRM device.
129
*/
130
void drm_shim_fd_register(int fd, struct shim_fd *shim_fd)
131
{
132
if (!shim_fd)
133
shim_fd = drm_shim_file_create(fd);
134
135
_mesa_hash_table_insert(shim_device.fd_map, (void *)(uintptr_t)(fd + 1), shim_fd);
136
}
137
138
struct shim_fd *
139
drm_shim_fd_lookup(int fd)
140
{
141
if (fd == -1)
142
return NULL;
143
144
struct hash_entry *entry =
145
_mesa_hash_table_search(shim_device.fd_map, (void *)(uintptr_t)(fd + 1));
146
147
if (!entry)
148
return NULL;
149
return entry->data;
150
}
151
152
/* ioctl used by drmGetVersion() */
153
static int
154
drm_shim_ioctl_version(int fd, unsigned long request, void *arg)
155
{
156
struct drm_version *args = arg;
157
const char *date = "20190320";
158
const char *desc = "shim";
159
160
args->version_major = shim_device.version_major;
161
args->version_minor = shim_device.version_minor;
162
args->version_patchlevel = shim_device.version_patchlevel;
163
164
if (args->name)
165
strncpy(args->name, shim_device.driver_name, args->name_len);
166
if (args->date)
167
strncpy(args->date, date, args->date_len);
168
if (args->desc)
169
strncpy(args->desc, desc, args->desc_len);
170
args->name_len = strlen(shim_device.driver_name);
171
args->date_len = strlen(date);
172
args->desc_len = strlen(desc);
173
174
return 0;
175
}
176
177
static int
178
drm_shim_ioctl_get_cap(int fd, unsigned long request, void *arg)
179
{
180
struct drm_get_cap *gc = arg;
181
182
switch (gc->capability) {
183
case DRM_CAP_PRIME:
184
case DRM_CAP_SYNCOBJ:
185
case DRM_CAP_SYNCOBJ_TIMELINE:
186
gc->value = 1;
187
return 0;
188
189
default:
190
fprintf(stderr, "DRM_IOCTL_GET_CAP: unhandled 0x%x\n",
191
(int)gc->capability);
192
return -1;
193
}
194
}
195
196
static int
197
drm_shim_ioctl_gem_close(int fd, unsigned long request, void *arg)
198
{
199
struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
200
struct drm_gem_close *c = arg;
201
202
if (!c->handle)
203
return 0;
204
205
mtx_lock(&shim_fd->handle_lock);
206
struct hash_entry *entry =
207
_mesa_hash_table_search(shim_fd->handles, (void *)(uintptr_t)c->handle);
208
if (!entry) {
209
mtx_unlock(&shim_fd->handle_lock);
210
return -EINVAL;
211
}
212
213
struct shim_bo *bo = entry->data;
214
_mesa_hash_table_remove(shim_fd->handles, entry);
215
drm_shim_bo_put(bo);
216
mtx_unlock(&shim_fd->handle_lock);
217
return 0;
218
}
219
220
static int
221
drm_shim_ioctl_syncobj_create(int fd, unsigned long request, void *arg)
222
{
223
struct drm_syncobj_create *create = arg;
224
225
create->handle = 1; /* 0 is invalid */
226
227
return 0;
228
}
229
230
static int
231
drm_shim_ioctl_stub(int fd, unsigned long request, void *arg)
232
{
233
return 0;
234
}
235
236
ioctl_fn_t core_ioctls[] = {
237
[_IOC_NR(DRM_IOCTL_VERSION)] = drm_shim_ioctl_version,
238
[_IOC_NR(DRM_IOCTL_GET_CAP)] = drm_shim_ioctl_get_cap,
239
[_IOC_NR(DRM_IOCTL_GEM_CLOSE)] = drm_shim_ioctl_gem_close,
240
[_IOC_NR(DRM_IOCTL_SYNCOBJ_CREATE)] = drm_shim_ioctl_syncobj_create,
241
[_IOC_NR(DRM_IOCTL_SYNCOBJ_DESTROY)] = drm_shim_ioctl_stub,
242
[_IOC_NR(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD)] = drm_shim_ioctl_stub,
243
[_IOC_NR(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE)] = drm_shim_ioctl_stub,
244
[_IOC_NR(DRM_IOCTL_SYNCOBJ_WAIT)] = drm_shim_ioctl_stub,
245
};
246
247
/**
248
* Implements the GEM core ioctls, and calls into driver-specific ioctls.
249
*/
250
int
251
drm_shim_ioctl(int fd, unsigned long request, void *arg)
252
{
253
ASSERTED int type = _IOC_TYPE(request);
254
int nr = _IOC_NR(request);
255
256
assert(type == DRM_IOCTL_BASE);
257
258
if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
259
int driver_nr = nr - DRM_COMMAND_BASE;
260
261
if (driver_nr < shim_device.driver_ioctl_count &&
262
shim_device.driver_ioctls[driver_nr]) {
263
return shim_device.driver_ioctls[driver_nr](fd, request, arg);
264
}
265
} else {
266
if (nr < ARRAY_SIZE(core_ioctls) && core_ioctls[nr]) {
267
return core_ioctls[nr](fd, request, arg);
268
}
269
}
270
271
if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
272
fprintf(stderr,
273
"DRM_SHIM: unhandled driver DRM ioctl %d (0x%08lx)\n",
274
nr - DRM_COMMAND_BASE, request);
275
} else {
276
fprintf(stderr,
277
"DRM_SHIM: unhandled core DRM ioctl 0x%X (0x%08lx)\n",
278
nr, request);
279
}
280
281
return -EINVAL;
282
}
283
284
void
285
drm_shim_bo_init(struct shim_bo *bo, size_t size)
286
{
287
288
mtx_lock(&shim_device.mem_lock);
289
bo->mem_addr = util_vma_heap_alloc(&shim_device.mem_heap, size, shim_page_size);
290
mtx_unlock(&shim_device.mem_lock);
291
assert(bo->mem_addr);
292
293
bo->size = size;
294
}
295
296
struct shim_bo *
297
drm_shim_bo_lookup(struct shim_fd *shim_fd, int handle)
298
{
299
if (!handle)
300
return NULL;
301
302
mtx_lock(&shim_fd->handle_lock);
303
struct hash_entry *entry =
304
_mesa_hash_table_search(shim_fd->handles, (void *)(uintptr_t)handle);
305
struct shim_bo *bo = entry ? entry->data : NULL;
306
mtx_unlock(&shim_fd->handle_lock);
307
308
if (bo)
309
p_atomic_inc(&bo->refcount);
310
311
return bo;
312
}
313
314
void
315
drm_shim_bo_get(struct shim_bo *bo)
316
{
317
p_atomic_inc(&bo->refcount);
318
}
319
320
void
321
drm_shim_bo_put(struct shim_bo *bo)
322
{
323
if (p_atomic_dec_return(&bo->refcount) == 0)
324
return;
325
326
if (shim_device.driver_bo_free)
327
shim_device.driver_bo_free(bo);
328
329
mtx_lock(&shim_device.mem_lock);
330
util_vma_heap_free(&shim_device.mem_heap, bo->mem_addr, bo->size);
331
mtx_unlock(&shim_device.mem_lock);
332
free(bo);
333
}
334
335
int
336
drm_shim_bo_get_handle(struct shim_fd *shim_fd, struct shim_bo *bo)
337
{
338
/* We should probably have some real datastructure for finding the free
339
* number.
340
*/
341
mtx_lock(&shim_fd->handle_lock);
342
for (int new_handle = 1; ; new_handle++) {
343
void *key = (void *)(uintptr_t)new_handle;
344
if (!_mesa_hash_table_search(shim_fd->handles, key)) {
345
drm_shim_bo_get(bo);
346
_mesa_hash_table_insert(shim_fd->handles, key, bo);
347
mtx_unlock(&shim_fd->handle_lock);
348
return new_handle;
349
}
350
}
351
mtx_unlock(&shim_fd->handle_lock);
352
353
return 0;
354
}
355
356
/* Creates an mmap offset for the BO in the DRM fd.
357
*
358
* XXX: We should be maintaining a u_mm allocator where the mmap offsets
359
* allocate the size of the BO and it can be used to look the BO back up.
360
* Instead, we just stuff the shim's pointer as the return value, and treat
361
* the incoming mmap offset on the DRM fd as a BO pointer. This doesn't work
362
* if someone tries to map a subset of the BO, but it's enough to get V3D
363
* working for now.
364
*/
365
uint64_t
366
drm_shim_bo_get_mmap_offset(struct shim_fd *shim_fd, struct shim_bo *bo)
367
{
368
return (uintptr_t)bo;
369
}
370
371
/* For mmap() on the DRM fd, look up the BO from the "offset" and map the BO's
372
* fd.
373
*/
374
void *
375
drm_shim_mmap(struct shim_fd *shim_fd, size_t length, int prot, int flags,
376
int fd, off_t offset)
377
{
378
struct shim_bo *bo = (void *)(uintptr_t)offset;
379
380
/* The offset we pass to mmap must be aligned to the page size */
381
assert((bo->mem_addr & (shim_page_size - 1)) == 0);
382
383
return mmap(NULL, length, prot, flags, shim_device.mem_fd, bo->mem_addr);
384
}
385
386