v3d: Introduce a DRM shim for calling out to the simulator.
authorEric Anholt <eric@anholt.net>
Wed, 26 Sep 2018 02:15:45 +0000 (19:15 -0700)
committerEric Anholt <eric@anholt.net>
Thu, 25 Jul 2019 15:56:19 +0000 (08:56 -0700)
The goal is to enable testing of parts of drivers without depending on any
particular kernel version or hardware being present.

Simply set LD_PRELOAD=$PREFIX/lib/libv3d_drm_shim.so in your environment,
and we'll fake a /dev/dri/renderD128 (or whatever the next available node
is) using v3dv3.  That node can then be used with the surfaceless or gbm
EGL platforms.

Acked-by: Iago Toral Quiroga <itoral@igalia.com>
15 files changed:
meson.build
meson_options.txt
src/broadcom/drm-shim/README.md [new file with mode: 0644]
src/broadcom/drm-shim/meson.build [new file with mode: 0644]
src/broadcom/drm-shim/v3d.c [new file with mode: 0644]
src/broadcom/drm-shim/v3d.h [new file with mode: 0644]
src/broadcom/drm-shim/v3d_noop.c [new file with mode: 0644]
src/broadcom/drm-shim/v3dx.c [new file with mode: 0644]
src/broadcom/meson.build
src/drm-shim/README.md [new file with mode: 0644]
src/drm-shim/device.c [new file with mode: 0644]
src/drm-shim/drm_shim.c [new file with mode: 0644]
src/drm-shim/drm_shim.h [new file with mode: 0644]
src/drm-shim/meson.build [new file with mode: 0644]
src/meson.build

index 804b921b1a5fda66d2688cf21e497f898f2fe52a..f4c013a5319797a16109054277dfe6dfb6f50234 100644 (file)
@@ -55,7 +55,16 @@ with_osmesa = get_option('osmesa')
 with_swr_arches = get_option('swr-arches')
 with_tools = get_option('tools')
 if with_tools.contains('all')
-  with_tools = ['etnaviv', 'freedreno', 'glsl', 'intel', 'nir', 'nouveau', 'xvmc']
+  with_tools = [
+    'drm-shim',
+    'etnaviv',
+    'freedreno',
+    'glsl',
+    'intel',
+    'nir',
+    'nouveau',
+    'xvmc',
+  ]
 endif
 
 dri_drivers_path = get_option('dri-drivers-path')
index 5cbb85658a27a7be6466a0837fe29a4ff607ccc5..b768c15053cddd034b7cafeeb528ba27dc7106e2 100644 (file)
@@ -310,7 +310,7 @@ option(
   'tools',
   type : 'array',
   value : [],
-  choices : ['etnaviv', 'freedreno', 'glsl', 'intel', 'intel-ui', 'nir', 'nouveau', 'xvmc', 'lima', 'all'],
+  choices : ['drm-shim', 'etnaviv', 'freedreno', 'glsl', 'intel', 'intel-ui', 'nir', 'nouveau', 'xvmc', 'lima', 'all'],
   description : 'List of tools to build. (Note: `intel-ui` selects `intel`)',
 )
 option(
diff --git a/src/broadcom/drm-shim/README.md b/src/broadcom/drm-shim/README.md
new file mode 100644 (file)
index 0000000..dde21c1
--- /dev/null
@@ -0,0 +1,17 @@
+### v3d backend
+
+This implements some of v3d using the closed source v3dv3 tree's
+C/C++-based simulator.  All execution is synchronous.
+
+Export: `MESA_LOADER_DRIVER_OVERRIDE=v3d
+LD_PRELOAD=$prefix/lib/libv3d_drm_shim.so`.  The v3dv3 version exposed
+will depend on the v3dv3 build -- 3.3, 4.1, and 4.2 are supported.
+
+### v3d_noop backend
+
+This implements the minimum of v3d in order to make shader-db work.
+The submit ioctl is stubbed out to not execute anything.
+
+Export `MESA_LOADER_DRIVER_OVERRIDE=v3d
+LD_PRELOAD=$prefix/lib/libv3d_noop_drm_shim.so`.  This will be a V3D
+4.2 device.
diff --git a/src/broadcom/drm-shim/meson.build b/src/broadcom/drm-shim/meson.build
new file mode 100644 (file)
index 0000000..2c2e4c5
--- /dev/null
@@ -0,0 +1,62 @@
+# Copyright © 2019 Broadcom
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+libv3d_noop_drm_shim = shared_library(
+  ['v3d_noop_drm_shim'],
+  'v3d_noop.c',
+  include_directories: inc_common,
+  dependencies: dep_drm_shim,
+  c_args : c_vis_args,
+  install : true,
+)
+
+dep_v3dv3 = dependency('v3dv3', required: false)
+if dep_v3dv3.found()
+  v3dv3_c_args = '-DUSE_V3D_SIMULATOR'
+
+  inc_gallium_v3d = include_directories('../../gallium/drivers/v3d')
+
+  per_version_libs = []
+  foreach ver : v3d_versions
+    per_version_libs += static_library(
+        'libv3d_drm_shim-v' + ver,
+        [
+                            'v3dx.c',
+                            v3d_xml_pack
+        ],
+        include_directories : [inc_common, inc_broadcom, inc_src, inc_gallium_v3d],
+        c_args : [c_vis_args, no_override_init_args, '-DV3D_VERSION=' + ver, v3dv3_c_args],
+        dependencies: [dep_valgrind, dep_thread, dep_v3dv3],
+    )
+  endforeach
+
+  libv3d_drm_shim = shared_library(
+    ['v3d_drm_shim'],
+    [
+      'v3d.c',
+      '../../gallium/drivers/v3d/v3d_simulator_wrapper.cpp',
+    ],
+    dependencies: [dep_dl, dep_drm_shim, dep_v3dv3],
+    link_with: [libmesa_util, per_version_libs],
+    include_directories : [inc_common, inc_broadcom, inc_gallium_v3d],
+    c_args : [c_vis_args, no_override_init_args, '-std=gnu99', v3dv3_c_args],
+    cpp_args : [v3dv3_c_args]
+  )
+endif
diff --git a/src/broadcom/drm-shim/v3d.c b/src/broadcom/drm-shim/v3d.c
new file mode 100644 (file)
index 0000000..e75657f
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2018 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include "drm-uapi/v3d_drm.h"
+#include "drm-shim/drm_shim.h"
+#include "v3d.h"
+#include "v3d_simulator_wrapper.h"
+
+static struct v3d_device_info devinfo;
+struct v3d_shim_device v3d = {
+        .devinfo = &devinfo
+};
+
+struct v3d_bo *v3d_bo_lookup(struct shim_fd *shim_fd, int handle)
+{
+        return v3d_bo(drm_shim_bo_lookup(shim_fd, handle));
+}
+
+int
+v3d_ioctl_wait_bo(int fd, unsigned long request, void *arg)
+{
+        /* No need to wait on anything yet, given that we submit
+         * synchronously.
+         */
+        return 0;
+}
+
+int
+v3d_ioctl_mmap_bo(int fd, unsigned long request, void *arg)
+{
+        struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+        struct drm_v3d_mmap_bo *map = arg;
+        struct shim_bo *bo = drm_shim_bo_lookup(shim_fd, map->handle);
+
+        map->offset = drm_shim_bo_get_mmap_offset(shim_fd, bo);
+
+        drm_shim_bo_put(bo);
+
+        return 0;
+}
+
+int
+v3d_ioctl_get_bo_offset(int fd, unsigned long request, void *arg)
+{
+        struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+        struct drm_v3d_get_bo_offset *get = arg;
+        struct v3d_bo *bo = v3d_bo_lookup(shim_fd, get->handle);
+
+        get->offset = bo->offset;
+
+        drm_shim_bo_put(&bo->base);
+
+        return 0;
+}
+
+void
+drm_shim_driver_init(void)
+{
+        shim_device.driver_name = "v3d";
+
+        drm_shim_override_file("OF_FULLNAME=/rdb/v3d\n"
+                               "OF_COMPATIBLE_N=1\n"
+                               "OF_COMPATIBLE_0=brcm,7278-v3d\n",
+                               "/sys/dev/char/%d:%d/device/uevent",
+                               DRM_MAJOR, render_node_minor);
+
+        v3d.hw = v3d_hw_auto_new(NULL);
+        v3d.devinfo->ver = v3d_hw_get_version(v3d.hw);
+
+        if (v3d.devinfo->ver >= 42)
+                v3d42_drm_shim_driver_init();
+        else if (v3d.devinfo->ver >= 41)
+                v3d41_drm_shim_driver_init();
+        else
+                v3d33_drm_shim_driver_init();
+}
diff --git a/src/broadcom/drm-shim/v3d.h b/src/broadcom/drm-shim/v3d.h
new file mode 100644 (file)
index 0000000..0712b8b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2018 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef DRM_SHIM_V3D_H
+#define DRM_SHIM_V3D_H
+
+#include "broadcom/common/v3d_device_info.h"
+#include "util/vma.h"
+
+struct drm_shim_fd;
+
+struct v3d_shim_device {
+        struct v3d_hw *hw;
+        struct v3d_device_info *devinfo;
+
+        /* Base virtual address of the heap. */
+        void *mem;
+        /* Base hardware address of the heap. */
+        uint32_t mem_base;
+        /* Size of the heap. */
+        size_t mem_size;
+
+        /* Allocator for the GPU virtual addresses. */
+        struct util_vma_heap heap;
+};
+extern struct v3d_shim_device v3d;
+
+struct v3d_bo {
+        struct shim_bo base;
+        uint64_t offset;
+        void *sim_vaddr;
+        void *gem_vaddr;
+};
+
+static inline struct v3d_bo *
+v3d_bo(struct shim_bo *bo)
+{
+        return (struct v3d_bo *)bo;
+}
+
+struct v3d_bo *v3d_bo_lookup(struct shim_fd *shim_fd, int handle);
+int v3d_ioctl_wait_bo(int fd, unsigned long request, void *arg);
+int v3d_ioctl_mmap_bo(int fd, unsigned long request, void *arg);
+int v3d_ioctl_get_bo_offset(int fd, unsigned long request, void *arg);
+
+void v3d33_drm_shim_driver_init(void);
+void v3d41_drm_shim_driver_init(void);
+void v3d42_drm_shim_driver_init(void);
+
+#endif /* DRM_SHIM_V3D_H */
diff --git a/src/broadcom/drm-shim/v3d_noop.c b/src/broadcom/drm-shim/v3d_noop.c
new file mode 100644 (file)
index 0000000..7c7d751
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright © 2018 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include "drm-uapi/v3d_drm.h"
+#include "drm-shim/drm_shim.h"
+
+struct v3d_bo {
+        struct shim_bo base;
+        uint32_t offset;
+};
+
+static struct v3d_bo *
+v3d_bo(struct shim_bo *bo)
+{
+        return (struct v3d_bo *)bo;
+}
+
+struct v3d_device {
+        uint32_t next_offset;
+};
+
+static struct v3d_device v3d = {
+        .next_offset = 0x1000,
+};
+
+static int
+v3d_ioctl_noop(int fd, unsigned long request, void *arg)
+{
+        return 0;
+}
+
+static int
+v3d_ioctl_create_bo(int fd, unsigned long request, void *arg)
+{
+        struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+        struct drm_v3d_create_bo *create = arg;
+        struct v3d_bo *bo = calloc(1, sizeof(*bo));
+
+        drm_shim_bo_init(&bo->base, create->size);
+
+        assert(UINT_MAX - v3d.next_offset > create->size);
+        bo->offset = v3d.next_offset;
+        v3d.next_offset += create->size;
+
+        create->offset = bo->offset;
+        create->handle = drm_shim_bo_get_handle(shim_fd, &bo->base);
+
+        drm_shim_bo_put(&bo->base);
+
+        return 0;
+}
+
+static int
+v3d_ioctl_get_bo_offset(int fd, unsigned long request, void *arg)
+{
+        struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+        struct drm_v3d_get_bo_offset *args = arg;
+        struct shim_bo *bo = drm_shim_bo_lookup(shim_fd, args->handle);
+
+        args->offset = v3d_bo(bo)->offset;
+
+        drm_shim_bo_put(bo);
+
+        return 0;
+}
+
+static int
+v3d_ioctl_mmap_bo(int fd, unsigned long request, void *arg)
+{
+        struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+        struct drm_v3d_mmap_bo *map = arg;
+        struct shim_bo *bo = drm_shim_bo_lookup(shim_fd, map->handle);
+
+        map->offset = drm_shim_bo_get_mmap_offset(shim_fd, bo);
+
+        drm_shim_bo_put(bo);
+
+        return 0;
+}
+
+static int
+v3d_ioctl_get_param(int fd, unsigned long request, void *arg)
+{
+        struct drm_v3d_get_param *gp = arg;
+        static const uint32_t v3d42_reg_map[] = {
+                [DRM_V3D_PARAM_V3D_UIFCFG] = 0x00000045,
+                [DRM_V3D_PARAM_V3D_HUB_IDENT1] = 0x000e1124,
+                [DRM_V3D_PARAM_V3D_HUB_IDENT2] = 0x00000100,
+                [DRM_V3D_PARAM_V3D_HUB_IDENT3] = 0x00000e00,
+                [DRM_V3D_PARAM_V3D_CORE0_IDENT0] = 0x04443356,
+                [DRM_V3D_PARAM_V3D_CORE0_IDENT1] = 0x81001422,
+                [DRM_V3D_PARAM_V3D_CORE0_IDENT2] = 0x40078121,
+        };
+
+        switch (gp->param) {
+        case DRM_V3D_PARAM_SUPPORTS_TFU:
+                gp->value = 1;
+                return 0;
+        default:
+                break;
+        }
+
+        if (gp->param < ARRAY_SIZE(v3d42_reg_map) && v3d42_reg_map[gp->param]) {
+                gp->value = v3d42_reg_map[gp->param];
+                return 0;
+        }
+
+        fprintf(stderr, "Unknown DRM_IOCTL_V3D_GET_PARAM %d\n", gp->param);
+        return -1;
+}
+
+static ioctl_fn_t driver_ioctls[] = {
+        [DRM_V3D_SUBMIT_CL] = v3d_ioctl_noop,
+        [DRM_V3D_SUBMIT_TFU] = v3d_ioctl_noop,
+        [DRM_V3D_WAIT_BO] = v3d_ioctl_noop,
+        [DRM_V3D_CREATE_BO] = v3d_ioctl_create_bo,
+        [DRM_V3D_GET_PARAM] = v3d_ioctl_get_param,
+        [DRM_V3D_GET_BO_OFFSET] = v3d_ioctl_get_bo_offset,
+        [DRM_V3D_MMAP_BO] = v3d_ioctl_mmap_bo,
+};
+
+void
+drm_shim_driver_init(void)
+{
+        shim_device.driver_name = "v3d";
+        shim_device.driver_ioctls = driver_ioctls;
+        shim_device.driver_ioctl_count = ARRAY_SIZE(driver_ioctls);
+
+        drm_shim_override_file("OF_FULLNAME=/rdb/v3d\n"
+                               "OF_COMPATIBLE_N=1\n"
+                               "OF_COMPATIBLE_0=brcm,7278-v3d\n",
+                               "/sys/dev/char/%d:%d/device/uevent",
+                               DRM_MAJOR, render_node_minor);
+}
diff --git a/src/broadcom/drm-shim/v3dx.c b/src/broadcom/drm-shim/v3dx.c
new file mode 100644 (file)
index 0000000..a22550a
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Copyright © 2014-2017 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* @file
+ *
+ * v3d driver code interacting v3dv3 simulator/fpga library.
+ *
+ * This is compiled per V3D version we support, since the register definitions
+ * conflict.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include "util/macros.h"
+#include "util/u_mm.h"
+#include "broadcom/common/v3d_macros.h"
+#include "v3d_simulator_wrapper.h"
+#include "drm-shim/drm_shim.h"
+#include "drm-uapi/v3d_drm.h"
+#include "v3d.h"
+
+#define HW_REGISTER_RO(x) (x)
+#define HW_REGISTER_RW(x) (x)
+#if V3D_VERSION >= 41
+#include "libs/core/v3d/registers/4.1.34.0/v3d.h"
+#else
+#include "libs/core/v3d/registers/3.3.0.0/v3d.h"
+#endif
+
+#define V3D_WRITE(reg, val) v3d_hw_write_reg(v3d.hw, reg, val)
+#define V3D_READ(reg) v3d_hw_read_reg(v3d.hw, reg)
+
+static void
+v3d_flush_l3()
+{
+        if (!v3d_hw_has_gca(v3d.hw))
+                return;
+
+#if V3D_VERSION < 40
+        uint32_t gca_ctrl = V3D_READ(V3D_GCA_CACHE_CTRL);
+
+        V3D_WRITE(V3D_GCA_CACHE_CTRL, gca_ctrl | V3D_GCA_CACHE_CTRL_FLUSH_SET);
+        V3D_WRITE(V3D_GCA_CACHE_CTRL, gca_ctrl & ~V3D_GCA_CACHE_CTRL_FLUSH_SET);
+#endif
+}
+
+/* Invalidates the L2 cache.  This is a read-only cache. */
+static void
+v3d_flush_l2(void)
+{
+        V3D_WRITE(V3D_CTL_0_L2CACTL,
+                  V3D_CTL_0_L2CACTL_L2CCLR_SET |
+                  V3D_CTL_0_L2CACTL_L2CENA_SET);
+}
+
+/* Invalidates texture L2 cachelines */
+static void
+v3d_flush_l2t(void)
+{
+        V3D_WRITE(V3D_CTL_0_L2TFLSTA, 0);
+        V3D_WRITE(V3D_CTL_0_L2TFLEND, ~0);
+        V3D_WRITE(V3D_CTL_0_L2TCACTL,
+                  V3D_CTL_0_L2TCACTL_L2TFLS_SET |
+                  (0 << V3D_CTL_0_L2TCACTL_L2TFLM_LSB));
+}
+
+/* Invalidates the slice caches.  These are read-only caches. */
+static void
+v3d_flush_slices(void)
+{
+        V3D_WRITE(V3D_CTL_0_SLCACTL, ~0);
+}
+
+static void
+v3d_flush_caches(void)
+{
+        v3d_flush_l3();
+        v3d_flush_l2();
+        v3d_flush_l2t();
+        v3d_flush_slices();
+}
+
+static void
+v3d_simulator_copy_in_handle(struct shim_fd *shim_fd, int handle)
+{
+        if (!handle)
+                return;
+
+        struct v3d_bo *bo = v3d_bo_lookup(shim_fd, handle);
+
+        memcpy(bo->sim_vaddr, bo->gem_vaddr, bo->base.size);
+}
+
+static void
+v3d_simulator_copy_out_handle(struct shim_fd *shim_fd, int handle)
+{
+        if (!handle)
+                return;
+
+        struct v3d_bo *bo = v3d_bo_lookup(shim_fd, handle);
+
+        memcpy(bo->gem_vaddr, bo->sim_vaddr, bo->base.size);
+}
+
+static int
+v3dX(v3d_ioctl_submit_cl)(int fd, unsigned long request, void *arg)
+{
+        struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+        struct drm_v3d_submit_cl *submit = arg;
+        uint32_t *bo_handles = (uint32_t *)(uintptr_t)submit->bo_handles;
+
+        for (int i = 0; i < submit->bo_handle_count; i++)
+                v3d_simulator_copy_in_handle(shim_fd, bo_handles[i]);
+
+        v3d_flush_caches();
+
+        if (submit->qma) {
+                V3D_WRITE(V3D_CLE_0_CT0QMA, submit->qma);
+                V3D_WRITE(V3D_CLE_0_CT0QMS, submit->qms);
+        }
+#if V3D_VERSION >= 41
+        if (submit->qts) {
+                V3D_WRITE(V3D_CLE_0_CT0QTS,
+                          V3D_CLE_0_CT0QTS_CTQTSEN_SET |
+                          submit->qts);
+        }
+#endif
+
+        fprintf(stderr, "submit %x..%x!\n", submit->bcl_start, submit->bcl_end);
+
+        V3D_WRITE(V3D_CLE_0_CT0QBA, submit->bcl_start);
+        V3D_WRITE(V3D_CLE_0_CT0QEA, submit->bcl_end);
+
+        /* Wait for bin to complete before firing render, as it seems the
+         * simulator doesn't implement the semaphores.
+         */
+        while (V3D_READ(V3D_CLE_0_CT0CA) !=
+               V3D_READ(V3D_CLE_0_CT0EA)) {
+                v3d_hw_tick(v3d.hw);
+        }
+
+        fprintf(stderr, "submit %x..%x!\n", submit->rcl_start, submit->rcl_end);
+
+        v3d_flush_caches();
+
+        V3D_WRITE(V3D_CLE_0_CT1QBA, submit->rcl_start);
+        V3D_WRITE(V3D_CLE_0_CT1QEA, submit->rcl_end);
+
+        while (V3D_READ(V3D_CLE_0_CT1CA) !=
+               V3D_READ(V3D_CLE_0_CT1EA)) {
+                v3d_hw_tick(v3d.hw);
+        }
+
+        for (int i = 0; i < submit->bo_handle_count; i++)
+                v3d_simulator_copy_out_handle(shim_fd, bo_handles[i]);
+
+        return 0;
+}
+
+static int
+v3dX(v3d_ioctl_submit_tfu)(int fd, unsigned long request, void *arg)
+{
+        struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+        struct drm_v3d_submit_tfu *submit = arg;
+
+        v3d_simulator_copy_in_handle(shim_fd, submit->bo_handles[0]);
+        v3d_simulator_copy_in_handle(shim_fd, submit->bo_handles[1]);
+        v3d_simulator_copy_in_handle(shim_fd, submit->bo_handles[2]);
+        v3d_simulator_copy_in_handle(shim_fd, submit->bo_handles[3]);
+
+        int last_vtct = V3D_READ(V3D_TFU_CS) & V3D_TFU_CS_CVTCT_SET;
+
+        V3D_WRITE(V3D_TFU_IIA, submit->iia);
+        V3D_WRITE(V3D_TFU_IIS, submit->iis);
+        V3D_WRITE(V3D_TFU_ICA, submit->ica);
+        V3D_WRITE(V3D_TFU_IUA, submit->iua);
+        V3D_WRITE(V3D_TFU_IOA, submit->ioa);
+        V3D_WRITE(V3D_TFU_IOS, submit->ios);
+        V3D_WRITE(V3D_TFU_COEF0, submit->coef[0]);
+        V3D_WRITE(V3D_TFU_COEF1, submit->coef[1]);
+        V3D_WRITE(V3D_TFU_COEF2, submit->coef[2]);
+        V3D_WRITE(V3D_TFU_COEF3, submit->coef[3]);
+
+        V3D_WRITE(V3D_TFU_ICFG, submit->icfg);
+
+        while ((V3D_READ(V3D_TFU_CS) & V3D_TFU_CS_CVTCT_SET) == last_vtct) {
+                v3d_hw_tick(v3d.hw);
+        }
+
+        v3d_simulator_copy_out_handle(shim_fd, submit->bo_handles[0]);
+
+        return 0;
+}
+
+static int
+v3dX(v3d_ioctl_create_bo)(int fd, unsigned long request, void *arg)
+{
+        struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+        struct drm_v3d_create_bo *create = arg;
+        struct v3d_bo *bo = calloc(1, sizeof(*bo));
+
+        drm_shim_bo_init(&bo->base, create->size);
+        bo->offset = util_vma_heap_alloc(&v3d.heap, create->size, 4096);
+        if (bo->offset == 0)
+                return -ENOMEM;
+
+        bo->sim_vaddr = v3d.mem + bo->offset - v3d.mem_base;
+#if 0
+        /* Place a mapping of the BO inside of the simulator's address space
+         * for V3D memory.  This lets us avoid copy in/out for simpenrose, but
+         * I'm betting we'll need something else for FPGA.
+         */
+        void *sim_addr = v3d.mem + bo->block->ofs;
+        void *mmap_ret = mmap(sim_addr, create->size, PROT_READ | PROT_WRITE,
+                              MAP_SHARED | MAP_FIXED, bo->base.fd, 0);
+        assert(mmap_ret == sim_addr);
+#else
+        /* Make a simulator-private mapping of the shim GEM object. */
+        bo->gem_vaddr = mmap(NULL, bo->base.size,
+                             PROT_READ | PROT_WRITE,
+                             MAP_SHARED,
+                             bo->base.fd, 0);
+        if (bo->gem_vaddr == MAP_FAILED) {
+                fprintf(stderr, "v3d: mmap of shim bo failed\n");
+                abort();
+        }
+#endif
+
+        create->offset = bo->offset;
+        create->handle = drm_shim_bo_get_handle(shim_fd, &bo->base);
+
+        drm_shim_bo_put(&bo->base);
+
+        return 0;
+}
+
+static int
+v3dX(v3d_ioctl_get_param)(int fd, unsigned long request, void *arg)
+{
+        struct drm_v3d_get_param *gp = arg;
+        static const uint32_t reg_map[] = {
+                [DRM_V3D_PARAM_V3D_UIFCFG] = V3D_HUB_CTL_UIFCFG,
+                [DRM_V3D_PARAM_V3D_HUB_IDENT1] = V3D_HUB_CTL_IDENT1,
+                [DRM_V3D_PARAM_V3D_HUB_IDENT2] = V3D_HUB_CTL_IDENT2,
+                [DRM_V3D_PARAM_V3D_HUB_IDENT3] = V3D_HUB_CTL_IDENT3,
+                [DRM_V3D_PARAM_V3D_CORE0_IDENT0] = V3D_CTL_0_IDENT0,
+                [DRM_V3D_PARAM_V3D_CORE0_IDENT1] = V3D_CTL_0_IDENT1,
+                [DRM_V3D_PARAM_V3D_CORE0_IDENT2] = V3D_CTL_0_IDENT2,
+        };
+
+        switch (gp->param) {
+        case DRM_V3D_PARAM_SUPPORTS_TFU:
+                gp->value = 1;
+                return 0;
+        }
+
+        if (gp->param < ARRAY_SIZE(reg_map) && reg_map[gp->param]) {
+                gp->value = V3D_READ(reg_map[gp->param]);
+                return 0;
+        }
+
+        fprintf(stderr, "Unknown DRM_IOCTL_V3D_GET_PARAM %d\n", gp->param);
+        return -1;
+}
+
+static ioctl_fn_t driver_ioctls[] = {
+        [DRM_V3D_SUBMIT_CL] = v3dX(v3d_ioctl_submit_cl),
+        [DRM_V3D_SUBMIT_TFU] = v3dX(v3d_ioctl_submit_tfu),
+        [DRM_V3D_WAIT_BO] = v3d_ioctl_wait_bo,
+        [DRM_V3D_CREATE_BO] = v3dX(v3d_ioctl_create_bo),
+        [DRM_V3D_GET_PARAM] = v3dX(v3d_ioctl_get_param),
+        [DRM_V3D_MMAP_BO] = v3d_ioctl_mmap_bo,
+        [DRM_V3D_GET_BO_OFFSET] = v3d_ioctl_get_bo_offset,
+};
+
+static void
+v3d_isr(uint32_t hub_status)
+{
+        /* Check the per-core bits */
+        if (hub_status & (1 << 0)) {
+                uint32_t core_status = V3D_READ(V3D_CTL_0_INT_STS);
+
+                if (core_status & V3D_CTL_0_INT_STS_INT_GMPV_SET) {
+                        fprintf(stderr, "GMP violation at 0x%08x\n",
+                                V3D_READ(V3D_GMP_0_VIO_ADDR));
+                        abort();
+                } else {
+                        fprintf(stderr,
+                                "Unexpected ISR with core status 0x%08x\n",
+                                core_status);
+                }
+                abort();
+        }
+
+        return;
+}
+
+static void
+v3dX(simulator_init_regs)(void)
+{
+#if V3D_VERSION == 33
+        /* Set OVRTMUOUT to match kernel behavior.
+         *
+         * This means that the texture sampler uniform configuration's tmu
+         * output type field is used, instead of using the hardware default
+         * behavior based on the texture type.  If you want the default
+         * behavior, you can still put "2" in the indirect texture state's
+         * output_type field.
+         */
+        V3D_WRITE(V3D_CTL_0_MISCCFG, V3D_CTL_1_MISCCFG_OVRTMUOUT_SET);
+#endif
+
+        uint32_t core_interrupts = V3D_CTL_0_INT_STS_INT_GMPV_SET;
+        V3D_WRITE(V3D_CTL_0_INT_MSK_SET, ~core_interrupts);
+        V3D_WRITE(V3D_CTL_0_INT_MSK_CLR, core_interrupts);
+
+        v3d_hw_set_isr(v3d.hw, v3d_isr);
+}
+
+static void
+v3d_bo_free(struct shim_bo *shim_bo)
+{
+        struct v3d_bo *bo = v3d_bo(shim_bo);
+
+        if (bo->gem_vaddr)
+                munmap(bo->gem_vaddr, shim_bo->size);
+
+        util_vma_heap_free(&v3d.heap, bo->offset, bo->base.size);
+}
+
+void
+v3dX(drm_shim_driver_init)(void)
+{
+        shim_device.driver_ioctls = driver_ioctls;
+        shim_device.driver_ioctl_count = ARRAY_SIZE(driver_ioctls);
+
+        shim_device.driver_bo_free = v3d_bo_free;
+
+        /* Allocate a gig of memory to play in. */
+        v3d_hw_alloc_mem(v3d.hw, 1024 * 1024 * 1024);
+        v3d.mem_base =
+                v3d_hw_get_mem(v3d.hw, &v3d.mem_size,
+                               &v3d.mem);
+        util_vma_heap_init(&v3d.heap, 4096, v3d.mem_size - 4096);
+
+        v3dX(simulator_init_regs)();
+}
index b744e7ed81beee6e438c2e36b12526bbeb9e48b7..57f0d889b25e32c841e9fe1e74e0222450ad0502 100644 (file)
@@ -30,6 +30,10 @@ if with_gallium_v3d
   subdir('qpu')
 endif
 
+if with_tools.contains('drm-shim')
+  subdir('drm-shim')
+endif
+
 per_version_libs = []
 foreach ver : v3d_versions
   per_version_libs += static_library(
diff --git a/src/drm-shim/README.md b/src/drm-shim/README.md
new file mode 100644 (file)
index 0000000..031a421
--- /dev/null
@@ -0,0 +1,34 @@
+# DRM shim - Fake GEM kernel drivers in userspace for CI
+
+On CI systems where we don't control the kernel, it would be nice to
+be able to present either no-op GEM devices (for shader-db runs) or
+simulator-backed GEM devices (for testing against a software simulator
+or FPGA).  This lets us do that by intercepting libc calls and
+exposing render nodes.
+
+## Limitations
+
+- Doesn't know how to handle DRM fds getting passed over the wire from
+  X11 (Could we use kmsro to support the X11 case?).
+- libc interception is rather glibc-specific and fragile.
+- Can easily break gdb if the libc interceptor code is what's broken.
+  (ulimit -c unlimited and doing gdb on the core after the fact can
+  help)
+
+## Using
+
+You choose the backend by setting `LD_PRELOAD` to the shim you want.
+Since this will effectively fake another DRM device to your system,
+you may need some work on your userspace to get your test application
+to use it if it's not the only DRM device present.  Setting
+`DRM_SHIM_DEBUG=1` in the environment will print out what path the
+shim initialized on.
+
+For piglit tests, you can set:
+
+```
+PIGLIT_PLATFORM=gbm
+WAFFLE_GBM_DEVICE=<path from DRM_SHIM_DEBUG>
+```
+
+See your drm-shim backend's README for details on how to use it.
diff --git a/src/drm-shim/device.c b/src/drm-shim/device.c
new file mode 100644 (file)
index 0000000..fe1cbbb
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright © 2018 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ *
+ * Implements core GEM support (particularly ioctls) underneath the libc ioctl
+ * wrappers, and calls into the driver-specific code as necessary.
+ */
+
+#include <c11/threads.h>
+#include <errno.h>
+#include <linux/memfd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "drm-uapi/drm.h"
+#include "drm_shim.h"
+#include "util/hash_table.h"
+#include "util/u_atomic.h"
+
+static mtx_t handle_lock = _MTX_INITIALIZER_NP;
+
+#ifndef HAVE_MEMFD_CREATE
+#include <sys/syscall.h>
+
+static inline int
+memfd_create(const char *name, unsigned int flags)
+{
+   return syscall(SYS_memfd_create, name, flags);
+}
+#endif
+
+/* Global state for the shim shared between libc, core, and driver. */
+struct shim_device shim_device;
+
+static uint32_t
+uint_key_hash(const void *key)
+{
+   return (uintptr_t)key;
+}
+
+static bool
+uint_key_compare(const void *a, const void *b)
+{
+   return a == b;
+}
+
+/**
+ * Called when the first libc shim is called, to initialize GEM simulation
+ * state (other than the shims themselves).
+ */
+void
+drm_shim_device_init(void)
+{
+   shim_device.fd_map = _mesa_hash_table_create(NULL,
+                                                uint_key_hash,
+                                                uint_key_compare);
+
+   drm_shim_driver_init();
+}
+
+static struct shim_fd *
+drm_shim_file_create(int fd)
+{
+   struct shim_fd *shim_fd = calloc(1, sizeof(*shim_fd));
+
+   shim_fd->fd = fd;
+   shim_fd->handles = _mesa_hash_table_create(NULL,
+                                              uint_key_hash,
+                                              uint_key_compare);
+
+   return shim_fd;
+}
+
+/**
+ * Called when the libc shims have interposed an open or dup of our simulated
+ * DRM device.
+ */
+void drm_shim_fd_register(int fd, struct shim_fd *shim_fd)
+{
+   if (!shim_fd)
+      shim_fd = drm_shim_file_create(fd);
+
+   _mesa_hash_table_insert(shim_device.fd_map, (void *)(uintptr_t)(fd + 1), shim_fd);
+}
+
+struct shim_fd *
+drm_shim_fd_lookup(int fd)
+{
+   if (fd == -1)
+      return NULL;
+
+   struct hash_entry *entry =
+      _mesa_hash_table_search(shim_device.fd_map, (void *)(uintptr_t)(fd + 1));
+
+   if (!entry)
+      return NULL;
+   return entry->data;
+}
+
+/* ioctl used by drmGetVersion() */
+static int
+drm_shim_ioctl_version(int fd, unsigned long request, void *arg)
+{
+   struct drm_version *args = arg;
+   const char *date = "20190320";
+   const char *desc = "shim";
+
+   if (args->name)
+      strncpy(args->name, shim_device.driver_name, args->name_len);
+   if (args->date)
+      strncpy(args->date, date, args->date_len);
+   if (args->desc)
+      strncpy(args->desc, desc, args->desc_len);
+   args->name_len = strlen(shim_device.driver_name);
+   args->date_len = strlen(date);
+   args->desc_len = strlen(desc);
+
+   return 0;
+}
+
+static int
+drm_shim_ioctl_get_cap(int fd, unsigned long request, void *arg)
+{
+   struct drm_get_cap *gc = arg;
+
+   switch (gc->capability) {
+   case DRM_CAP_PRIME:
+   case DRM_CAP_SYNCOBJ:
+      gc->value = 1;
+      return 0;
+
+   default:
+      fprintf(stderr, "DRM_IOCTL_GET_CAP: unhandled 0x%x\n",
+              (int)gc->capability);
+      return -1;
+   }
+}
+
+static int
+drm_shim_ioctl_gem_close(int fd, unsigned long request, void *arg)
+{
+   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+   struct drm_gem_close *c = arg;
+
+   if (!c->handle)
+      return 0;
+
+   mtx_lock(&handle_lock);
+   struct hash_entry *entry =
+      _mesa_hash_table_search(shim_fd->handles, (void *)(uintptr_t)c->handle);
+   if (!entry) {
+      mtx_unlock(&handle_lock);
+      return -EINVAL;
+   }
+
+   struct shim_bo *bo = entry->data;
+   _mesa_hash_table_remove(shim_fd->handles, entry);
+   drm_shim_bo_put(bo);
+   mtx_unlock(&handle_lock);
+   return 0;
+}
+
+static int
+drm_shim_ioctl_stub(int fd, unsigned long request, void *arg)
+{
+   return 0;
+}
+
+ioctl_fn_t core_ioctls[] = {
+   [_IOC_NR(DRM_IOCTL_VERSION)] = drm_shim_ioctl_version,
+   [_IOC_NR(DRM_IOCTL_GET_CAP)] = drm_shim_ioctl_get_cap,
+   [_IOC_NR(DRM_IOCTL_GEM_CLOSE)] = drm_shim_ioctl_gem_close,
+   [_IOC_NR(DRM_IOCTL_SYNCOBJ_CREATE)] = drm_shim_ioctl_stub,
+   [_IOC_NR(DRM_IOCTL_SYNCOBJ_DESTROY)] = drm_shim_ioctl_stub,
+   [_IOC_NR(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD)] = drm_shim_ioctl_stub,
+   [_IOC_NR(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE)] = drm_shim_ioctl_stub,
+};
+
+/**
+ * Implements the GEM core ioctls, and calls into driver-specific ioctls.
+ */
+int
+drm_shim_ioctl(int fd, unsigned long request, void *arg)
+{
+   int type = _IOC_TYPE(request);
+   int nr = _IOC_NR(request);
+
+   assert(type == DRM_IOCTL_BASE);
+
+   if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
+      int driver_nr = nr - DRM_COMMAND_BASE;
+
+      if (driver_nr < shim_device.driver_ioctl_count &&
+          shim_device.driver_ioctls[driver_nr]) {
+         return shim_device.driver_ioctls[driver_nr](fd, request, arg);
+      }
+   } else {
+      if (nr < ARRAY_SIZE(core_ioctls) && core_ioctls[nr]) {
+         return core_ioctls[nr](fd, request, arg);
+      }
+   }
+
+   if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) {
+      fprintf(stderr,
+              "DRM_SHIM: unhandled driver DRM ioctl %d (0x%08lx)\n",
+              nr - DRM_COMMAND_BASE, request);
+   } else {
+      fprintf(stderr,
+              "DRM_SHIM: unhandled core DRM ioctl 0x%X (0x%08lx)\n",
+              nr, request);
+   }
+
+   abort();
+}
+
+void
+drm_shim_bo_init(struct shim_bo *bo, size_t size)
+{
+   bo->size = size;
+   bo->fd = memfd_create("shim bo", MFD_CLOEXEC);
+   if (bo->fd == -1) {
+      fprintf(stderr, "Failed to create BO: %s\n", strerror(errno));
+      abort();
+   }
+
+   if (ftruncate(bo->fd, size) == -1) {
+      fprintf(stderr, "Failed to size BO: %s\n", strerror(errno));
+      abort();
+   }
+}
+
+struct shim_bo *
+drm_shim_bo_lookup(struct shim_fd *shim_fd, int handle)
+{
+   if (!handle)
+      return NULL;
+
+   mtx_lock(&handle_lock);
+   struct hash_entry *entry =
+      _mesa_hash_table_search(shim_fd->handles, (void *)(uintptr_t)handle);
+   struct shim_bo *bo = entry ? entry->data : NULL;
+   mtx_unlock(&handle_lock);
+
+   if (bo)
+      p_atomic_inc(&bo->refcount);
+
+   return bo;
+}
+
+void
+drm_shim_bo_get(struct shim_bo *bo)
+{
+   p_atomic_inc(&bo->refcount);
+}
+
+void
+drm_shim_bo_put(struct shim_bo *bo)
+{
+   if (p_atomic_dec_return(&bo->refcount) == 0)
+      return;
+
+   if (shim_device.driver_bo_free)
+      shim_device.driver_bo_free(bo);
+   close(bo->fd);
+   free(bo);
+}
+
+int
+drm_shim_bo_get_handle(struct shim_fd *shim_fd, struct shim_bo *bo)
+{
+   /* We should probably have some real datastructure for finding the free
+    * number.
+    */
+   mtx_lock(&handle_lock);
+   for (int new_handle = 1; ; new_handle++) {
+      void *key = (void *)(uintptr_t)new_handle;
+      if (!_mesa_hash_table_search(shim_fd->handles, key)) {
+         drm_shim_bo_get(bo);
+         _mesa_hash_table_insert(shim_fd->handles, key, bo);
+         mtx_unlock(&handle_lock);
+         return new_handle;
+      }
+   }
+   mtx_unlock(&handle_lock);
+
+   return 0;
+}
+
+/* Creates an mmap offset for the BO in the DRM fd.
+ *
+ * XXX: We should be maintaining a u_mm allocator where the mmap offsets
+ * allocate the size of the BO and it can be used to look the BO back up.
+ * Instead, we just stuff the shim's pointer as the return value, and treat
+ * the incoming mmap offset on the DRM fd as a BO pointer.  This doesn't work
+ * if someone tries to map a subset of the BO, but it's enough to get V3D
+ * working for now.
+ */
+uint64_t
+drm_shim_bo_get_mmap_offset(struct shim_fd *shim_fd, struct shim_bo *bo)
+{
+   return (uintptr_t)bo;
+}
+
+/* For mmap() on the DRM fd, look up the BO from the "offset" and map the BO's
+ * fd.
+ */
+void *
+drm_shim_mmap(struct shim_fd *shim_fd, size_t length, int prot, int flags,
+              int fd, off_t offset)
+{
+   struct shim_bo *bo = (void *)(uintptr_t)offset;
+
+   return mmap(NULL, length, prot, flags, bo->fd, 0);
+}
diff --git a/src/drm-shim/drm_shim.c b/src/drm-shim/drm_shim.c
new file mode 100644 (file)
index 0000000..7c7d5f0
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright © 2018 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @file
+ *
+ * Implements wrappers of libc functions to fake having a DRM device that
+ * isn't actually present in the kernel.
+ */
+
+/* Prevent glibc from defining open64 when we want to alias it. */
+#undef _FILE_OFFSET_BITS
+#define _LARGEFILE64_SOURCE
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <c11/threads.h>
+#include <drm-uapi/drm.h>
+
+#include "util/set.h"
+#include "util/u_debug.h"
+#include "drm_shim.h"
+
+#define REAL_FUNCTION_POINTER(x) typeof(x) *real_##x
+
+static mtx_t shim_lock = _MTX_INITIALIZER_NP;
+struct set *opendir_set;
+bool drm_shim_debug;
+
+/* If /dev/dri doesn't exist, we'll need an arbitrary pointer that wouldn't be
+ * returned by any other opendir() call so we can return just our fake node.
+ */
+DIR *fake_dev_dri = (void *)&opendir_set;
+
+/* XXX: implement REAL_FUNCTION_POINTER(close); */
+REAL_FUNCTION_POINTER(closedir);
+REAL_FUNCTION_POINTER(dup);
+REAL_FUNCTION_POINTER(fcntl);
+REAL_FUNCTION_POINTER(fopen);
+REAL_FUNCTION_POINTER(ioctl);
+REAL_FUNCTION_POINTER(mmap);
+REAL_FUNCTION_POINTER(open);
+REAL_FUNCTION_POINTER(opendir);
+REAL_FUNCTION_POINTER(readdir);
+REAL_FUNCTION_POINTER(readdir64);
+REAL_FUNCTION_POINTER(readlink);
+REAL_FUNCTION_POINTER(__xstat);
+REAL_FUNCTION_POINTER(__xstat64);
+REAL_FUNCTION_POINTER(__fxstat);
+REAL_FUNCTION_POINTER(__fxstat64);
+
+/* Full path of /dev/dri/renderD* */
+static char *render_node_path;
+/* renderD* */
+static char *render_node_dirent_name;
+/* /sys/dev/char/major:minor/device/subsystem */
+static char *subsystem_path;
+int render_node_minor = -1;
+
+struct file_override {
+   const char *path;
+   char *contents;
+};
+static struct file_override file_overrides[10];
+static int file_overrides_count;
+
+/* Come up with a filename for a render node that doesn't actually exist on
+ * the system.
+ */
+static void
+get_dri_render_node_minor(void)
+{
+   for (int i = 0; i < 10; i++) {
+      int minor = 128 + i;
+      asprintf(&render_node_dirent_name, "renderD%d", minor);
+      asprintf(&render_node_path, "/dev/dri/%s",
+               render_node_dirent_name);
+      struct stat st;
+      if (stat(render_node_path, &st) == -1) {
+
+         render_node_minor = minor;
+         return;
+      }
+   }
+
+   fprintf(stderr, "Couldn't find a spare render node slot\n");
+}
+
+static void *get_function_pointer(const char *name)
+{
+   void *func = dlsym(RTLD_NEXT, name);
+   if (!func) {
+      fprintf(stderr, "Failed to resolve %s\n", name);
+      abort();
+   }
+   return func;
+}
+
+#define GET_FUNCTION_POINTER(x) real_##x = get_function_pointer(#x)
+
+void
+drm_shim_override_file(const char *contents, const char *path_format, ...)
+{
+   assert(file_overrides_count < ARRAY_SIZE(file_overrides));
+
+   char *path;
+   va_list ap;
+   va_start(ap, path_format);
+   vasprintf(&path, path_format, ap);
+   va_end(ap);
+
+   struct file_override *override = &file_overrides[file_overrides_count++];
+   override->path = path;
+   override->contents = strdup(contents);
+}
+
+static void
+destroy_shim(void)
+{
+   _mesa_set_destroy(opendir_set, NULL);
+   free(render_node_path);
+   free(render_node_dirent_name);
+   free(subsystem_path);
+}
+
+/* Initialization, which will be called from the first general library call
+ * that might need to be wrapped with the shim.
+ */
+static void
+init_shim(void)
+{
+   static bool inited = false;
+   drm_shim_debug = debug_get_bool_option("DRM_SHIM_DEBUG", false);
+
+   /* We can't lock this, because we recurse during initialization. */
+   if (inited)
+      return;
+
+   /* This comes first (and we're locked), to make sure we don't recurse
+    * during initialization.
+    */
+   inited = true;
+
+   opendir_set = _mesa_set_create(NULL,
+                                  _mesa_hash_string,
+                                  _mesa_key_string_equal);
+
+   GET_FUNCTION_POINTER(closedir);
+   GET_FUNCTION_POINTER(dup);
+   GET_FUNCTION_POINTER(fcntl);
+   GET_FUNCTION_POINTER(fopen);
+   GET_FUNCTION_POINTER(ioctl);
+   GET_FUNCTION_POINTER(mmap);
+   GET_FUNCTION_POINTER(open);
+   GET_FUNCTION_POINTER(opendir);
+   GET_FUNCTION_POINTER(readdir);
+   GET_FUNCTION_POINTER(readdir64);
+   GET_FUNCTION_POINTER(readlink);
+   GET_FUNCTION_POINTER(__xstat);
+   GET_FUNCTION_POINTER(__xstat64);
+   GET_FUNCTION_POINTER(__fxstat);
+   GET_FUNCTION_POINTER(__fxstat64);
+
+   get_dri_render_node_minor();
+
+   if (drm_shim_debug) {
+      fprintf(stderr, "Initializing DRM shim on %s\n",
+              render_node_path);
+   }
+
+   asprintf(&subsystem_path,
+            "/sys/dev/char/%d:%d/device/subsystem",
+            DRM_MAJOR, render_node_minor);
+
+   drm_shim_device_init();
+
+   atexit(destroy_shim);
+}
+
+/* Override libdrm's reading of various sysfs files for device enumeration. */
+PUBLIC FILE *fopen(const char *path, const char *mode)
+{
+   init_shim();
+
+   for (int i = 0; i < file_overrides_count; i++) {
+      if (strcmp(file_overrides[i].path, path) == 0) {
+         int fds[2];
+         pipe(fds);
+         write(fds[1], file_overrides[i].contents,
+               strlen(file_overrides[i].contents));
+         return fdopen(fds[0], "r");
+      }
+   }
+
+   return real_fopen(path, mode);
+}
+PUBLIC FILE *fopen64(const char *path, const char *mode)
+   __attribute__((alias("fopen")));
+
+/* Intercepts open(render_node_path) to redirect it to the simulator. */
+PUBLIC int open(const char *path, int flags, ...)
+{
+   init_shim();
+
+   va_list ap;
+   va_start(ap, flags);
+   mode_t mode = va_arg(ap, mode_t);
+   va_end(ap);
+
+   if (strcmp(path, render_node_path) != 0)
+      return real_open(path, flags, mode);
+
+   int fd = real_open("/dev/null", O_RDWR, 0);
+
+   drm_shim_fd_register(fd, NULL);
+
+   return fd;
+}
+PUBLIC int open64(const char*, int, ...) __attribute__((alias("open")));
+
+/* Fakes stat to return character device stuff for our fake render node. */
+PUBLIC int __xstat(int ver, const char *path, struct stat *st)
+{
+   init_shim();
+
+   /* Note: call real stat if we're in the process of probing for a free
+    * render node!
+    */
+   if (render_node_minor == -1)
+      return real___xstat(ver, path, st);
+
+   /* Fool libdrm's probe of whether the /sys dir for this char dev is
+    * there.
+    */
+   char *sys_dev_drm_dir;
+   asprintf(&sys_dev_drm_dir, "/sys/dev/char/%d:%d/device/drm",
+            DRM_MAJOR, render_node_minor);
+   if (strcmp(path, sys_dev_drm_dir) == 0) {
+      free(sys_dev_drm_dir);
+      return 0;
+   }
+   free(sys_dev_drm_dir);
+
+   if (strcmp(path, render_node_path) != 0)
+      return real___xstat(ver, path, st);
+
+   memset(st, 0, sizeof(*st));
+   st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
+   st->st_mode = S_IFCHR;
+
+   return 0;
+}
+
+/* Fakes stat to return character device stuff for our fake render node. */
+PUBLIC int __xstat64(int ver, const char *path, struct stat64 *st)
+{
+   init_shim();
+
+   /* Note: call real stat if we're in the process of probing for a free
+    * render node!
+    */
+   if (render_node_minor == -1)
+      return real___xstat64(ver, path, st);
+
+   /* Fool libdrm's probe of whether the /sys dir for this char dev is
+    * there.
+    */
+   char *sys_dev_drm_dir;
+   asprintf(&sys_dev_drm_dir, "/sys/dev/char/%d:%d/device/drm",
+            DRM_MAJOR, render_node_minor);
+   if (strcmp(path, sys_dev_drm_dir) == 0) {
+      free(sys_dev_drm_dir);
+      return 0;
+   }
+   free(sys_dev_drm_dir);
+
+   if (strcmp(path, render_node_path) != 0)
+      return real___xstat64(ver, path, st);
+
+   memset(st, 0, sizeof(*st));
+   st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
+   st->st_mode = S_IFCHR;
+
+   return 0;
+}
+
+/* Fakes fstat to return character device stuff for our fake render node. */
+PUBLIC int __fxstat(int ver, int fd, struct stat *st)
+{
+   init_shim();
+
+   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+
+   if (!shim_fd)
+      return real___fxstat(ver, fd, st);
+
+   memset(st, 0, sizeof(*st));
+   st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
+   st->st_mode = S_IFCHR;
+
+   return 0;
+}
+
+PUBLIC int __fxstat64(int ver, int fd, struct stat64 *st)
+{
+   init_shim();
+
+   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+
+   if (!shim_fd)
+      return real___fxstat64(ver, fd, st);
+
+   memset(st, 0, sizeof(*st));
+   st->st_rdev = makedev(DRM_MAJOR, render_node_minor);
+   st->st_mode = S_IFCHR;
+
+   return 0;
+}
+
+/* Tracks if the opendir was on /dev/dri. */
+PUBLIC DIR *
+opendir(const char *name)
+{
+   init_shim();
+
+   DIR *dir = real_opendir(name);
+   if (strcmp(name, "/dev/dri") == 0) {
+      if (!dir) {
+         /* If /dev/dri didn't exist, we still want to be able to return our
+          * fake /dev/dri/render* even though we probably can't
+          * mkdir("/dev/dri").  Return a fake DIR pointer for that.
+          */
+         dir = fake_dev_dri;
+      }
+
+      mtx_lock(&shim_lock);
+      _mesa_set_add(opendir_set, dir);
+      mtx_unlock(&shim_lock);
+   }
+
+   return dir;
+}
+
+/* If we've reached the end of the real directory list and we're
+ * looking at /dev/dri, add our render node to the list.
+ */
+PUBLIC struct dirent *
+readdir(DIR *dir)
+{
+   init_shim();
+
+   struct dirent *ent = NULL;
+
+   if (dir != fake_dev_dri)
+      ent = real_readdir(dir);
+   static struct dirent render_node_dirent = { 0 };
+
+   if (!ent) {
+      mtx_lock(&shim_lock);
+      if (_mesa_set_search(opendir_set, dir)) {
+         strcpy(render_node_dirent.d_name,
+                render_node_dirent_name);
+         ent = &render_node_dirent;
+         _mesa_set_remove_key(opendir_set, dir);
+      }
+      mtx_unlock(&shim_lock);
+   }
+
+   return ent;
+}
+
+/* If we've reached the end of the real directory list and we're
+ * looking at /dev/dri, add our render node to the list.
+ */
+PUBLIC struct dirent64 *
+readdir64(DIR *dir)
+{
+   init_shim();
+
+   struct dirent64 *ent = NULL;
+   if (dir != fake_dev_dri)
+      ent = real_readdir64(dir);
+   static struct dirent64 render_node_dirent = { 0 };
+
+   if (!ent) {
+      mtx_lock(&shim_lock);
+      if (_mesa_set_search(opendir_set, dir)) {
+         strcpy(render_node_dirent.d_name,
+                render_node_dirent_name);
+         ent = &render_node_dirent;
+         _mesa_set_remove_key(opendir_set, dir);
+      }
+      mtx_unlock(&shim_lock);
+   }
+
+   return ent;
+}
+
+/* Cleans up tracking of opendir("/dev/dri") */
+PUBLIC int
+closedir(DIR *dir)
+{
+   init_shim();
+
+   mtx_lock(&shim_lock);
+   _mesa_set_remove_key(opendir_set, dir);
+   mtx_unlock(&shim_lock);
+
+   if (dir != fake_dev_dri)
+      return real_closedir(dir);
+   else
+      return 0;
+}
+
+/* Handles libdrm's readlink to figure out what kind of device we have. */
+PUBLIC ssize_t
+readlink(const char *path, char *buf, size_t size)
+{
+   init_shim();
+
+   if (strcmp(path, subsystem_path) != 0)
+      return real_readlink(path, buf, size);
+   strncpy(buf, "/platform", size);
+   buf[size - 1] = 0;
+
+   return strlen(buf) + 1;
+}
+
+/* Main entrypoint to DRM drivers: the ioctl syscall.  We send all ioctls on
+ * our DRM fd to drm_shim_ioctl().
+ */
+PUBLIC int
+ioctl(int fd, unsigned long request, ...)
+{
+   init_shim();
+
+   va_list ap;
+   va_start(ap, request);
+   void *arg = va_arg(ap, void *);
+   va_end(ap);
+
+   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+   if (!shim_fd)
+      return real_ioctl(fd, request, arg);
+
+   return drm_shim_ioctl(fd, request, arg);
+}
+
+/* Gallium uses this to dup the incoming fd on gbm screen creation */
+PUBLIC int
+fcntl(int fd, int cmd, ...)
+{
+   init_shim();
+
+   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+
+   va_list ap;
+   va_start(ap, cmd);
+   void *arg = va_arg(ap, void *);
+   va_end(ap);
+
+   int ret = real_fcntl(fd, cmd, arg);
+
+   if (shim_fd && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC))
+      drm_shim_fd_register(ret, shim_fd);
+
+   return ret;
+}
+PUBLIC int fcntl64(int, int, ...)
+   __attribute__((alias("fcntl")));
+
+/* I wrote this when trying to fix gallium screen creation, leaving it around
+ * since it's probably good to have.
+ */
+PUBLIC int
+dup(int fd)
+{
+   init_shim();
+
+   int ret = real_dup(fd);
+
+   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+   if (shim_fd && ret >= 0)
+      drm_shim_fd_register(ret, shim_fd);
+
+   return ret;
+}
+
+PUBLIC void *
+mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
+{
+   init_shim();
+
+   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
+   if (shim_fd)
+      return drm_shim_mmap(shim_fd, length, prot, flags, fd, offset);
+
+   return real_mmap(addr, length, prot, flags, fd, offset);
+}
+PUBLIC void *mmap64(void*, size_t, int, int, int, off_t)
+   __attribute__((alias("mmap")));
diff --git a/src/drm-shim/drm_shim.h b/src/drm-shim/drm_shim.h
new file mode 100644 (file)
index 0000000..b14353a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2018 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "util/macros.h"
+#include "util/hash_table.h"
+
+#ifdef __linux__
+#define DRM_MAJOR 226
+#endif
+
+typedef int (*ioctl_fn_t)(int fd, unsigned long request, void *arg);
+
+struct shim_bo;
+
+struct shim_device {
+   /* Mapping from int fd to struct shim_fd *. */
+   struct hash_table *fd_map;
+
+   int (**driver_ioctls)(int fd, unsigned long request, void *arg);
+   int driver_ioctl_count;
+
+   void (*driver_bo_free)(struct shim_bo *bo);
+
+   /* Returned by drmGetVersion(). */
+   const char *driver_name;
+};
+
+extern struct shim_device shim_device;
+
+struct shim_fd {
+   int fd;
+   /* mapping from int gem handle to struct shim_bo *. */
+   struct hash_table *handles;
+};
+
+struct shim_bo {
+   int fd;
+   void *map;
+   int refcount;
+   uint32_t size;
+};
+
+/* Core support. */
+extern int render_node_minor;
+void drm_shim_device_init(void);
+void drm_shim_override_file(const char *contents,
+                            const char *path_format, ...) PRINTFLIKE(2, 3);
+void drm_shim_fd_register(int fd, struct shim_fd *shim_fd);
+struct shim_fd *drm_shim_fd_lookup(int fd);
+int drm_shim_ioctl(int fd, unsigned long request, void *arg);
+void *drm_shim_mmap(struct shim_fd *shim_fd, size_t length, int prot, int flags,
+                    int fd, off_t offset);
+
+void drm_shim_bo_init(struct shim_bo *bo, size_t size);
+void drm_shim_bo_get(struct shim_bo *bo);
+void drm_shim_bo_put(struct shim_bo *bo);
+struct shim_bo *drm_shim_bo_lookup(struct shim_fd *shim_fd, int handle);
+int drm_shim_bo_get_handle(struct shim_fd *shim_fd, struct shim_bo *bo);
+uint64_t drm_shim_bo_get_mmap_offset(struct shim_fd *shim_fd,
+                                     struct shim_bo *bo);
+
+/* driver-specific hooks. */
+void drm_shim_driver_init(void);
diff --git a/src/drm-shim/meson.build b/src/drm-shim/meson.build
new file mode 100644 (file)
index 0000000..1638c5a
--- /dev/null
@@ -0,0 +1,34 @@
+# Copyright © 2019 Broadcom
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+drm_shim = static_library(
+  ['drm_shim'],
+  [
+    'device.c',
+    'drm_shim.c',
+  ],
+  link_with: libmesa_util,
+  include_directories: [inc_common],
+  dependencies: [dep_dl],
+  c_args : [c_vis_args, '-std=gnu99'],
+)
+dep_drm_shim = declare_dependency(
+  link_with: drm_shim
+)
index 0dcd7eef65ddfd8038b48607c6d6619dd5fcbb24..d86c0ec21a6184ab4f71e06208982efe60e0d0d9 100644 (file)
@@ -51,6 +51,9 @@ subdir('util')
 subdir('mapi')
 # TODO: opengl
 subdir('compiler')
+if with_tools.contains('drm-shim')
+  subdir('drm-shim')
+endif
 subdir('imgui')
 if with_platform_wayland
   subdir('egl/wayland/wayland-drm')