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')
'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(
--- /dev/null
+### 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.
--- /dev/null
+# 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
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)();
+}
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(
--- /dev/null
+# 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.
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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")));
--- /dev/null
+/*
+ * 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);
--- /dev/null
+# 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
+)
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')