17ec681f3Smrg/*
27ec681f3Smrg * Copyright © 2021 Ilia Mirkin
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
217ec681f3Smrg * DEALINGS IN THE SOFTWARE.
227ec681f3Smrg */
237ec681f3Smrg
247ec681f3Smrg#include <limits.h>
257ec681f3Smrg#include <stdio.h>
267ec681f3Smrg#include <stdint.h>
277ec681f3Smrg#include <stdlib.h>
287ec681f3Smrg#include <sys/ioctl.h>
297ec681f3Smrg#include <nouveau_drm.h>
307ec681f3Smrg#include "drm-shim/drm_shim.h"
317ec681f3Smrg#include "util//u_math.h"
327ec681f3Smrg
337ec681f3Smrgbool drm_shim_driver_prefers_first_render_node = true;
347ec681f3Smrg
357ec681f3Smrgstruct nouveau_device {
367ec681f3Smrg   uint64_t next_offset;
377ec681f3Smrg};
387ec681f3Smrg
397ec681f3Smrgstatic struct nouveau_device nouveau = {
407ec681f3Smrg   .next_offset = 0x1000,
417ec681f3Smrg};
427ec681f3Smrg
437ec681f3Smrgstruct nouveau_shim_bo {
447ec681f3Smrg   struct shim_bo base;
457ec681f3Smrg   uint64_t offset;
467ec681f3Smrg};
477ec681f3Smrg
487ec681f3Smrgstatic struct nouveau_shim_bo *
497ec681f3Smrgnouveau_shim_bo(struct shim_bo *bo)
507ec681f3Smrg{
517ec681f3Smrg   return (struct nouveau_shim_bo *)bo;
527ec681f3Smrg}
537ec681f3Smrg
547ec681f3Smrgstruct nouveau_device_info {
557ec681f3Smrg   uint32_t chip_id;
567ec681f3Smrg};
577ec681f3Smrg
587ec681f3Smrgstatic struct nouveau_device_info device_info;
597ec681f3Smrg
607ec681f3Smrgstatic int
617ec681f3Smrgnouveau_ioctl_noop(int fd, unsigned long request, void *arg)
627ec681f3Smrg{
637ec681f3Smrg   return 0;
647ec681f3Smrg}
657ec681f3Smrg
667ec681f3Smrgstatic int
677ec681f3Smrgnouveau_ioctl_gem_new(int fd, unsigned long request, void *arg)
687ec681f3Smrg{
697ec681f3Smrg   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
707ec681f3Smrg   struct drm_nouveau_gem_new *create = arg;
717ec681f3Smrg   struct nouveau_shim_bo *bo = calloc(1, sizeof(*bo));
727ec681f3Smrg
737ec681f3Smrg   drm_shim_bo_init(&bo->base, create->info.size);
747ec681f3Smrg
757ec681f3Smrg   assert(ULONG_MAX - nouveau.next_offset > create->info.size);
767ec681f3Smrg
777ec681f3Smrg   create->info.handle = drm_shim_bo_get_handle(shim_fd, &bo->base);
787ec681f3Smrg   create->info.map_handle = drm_shim_bo_get_mmap_offset(shim_fd, &bo->base);
797ec681f3Smrg
807ec681f3Smrg   if (create->align != 0)
817ec681f3Smrg      nouveau.next_offset = align64(nouveau.next_offset, create->align);
827ec681f3Smrg   create->info.offset = nouveau.next_offset;
837ec681f3Smrg   nouveau.next_offset += create->info.size;
847ec681f3Smrg
857ec681f3Smrg   bo->offset = create->info.offset;
867ec681f3Smrg
877ec681f3Smrg   drm_shim_bo_put(&bo->base);
887ec681f3Smrg
897ec681f3Smrg   return 0;
907ec681f3Smrg}
917ec681f3Smrg
927ec681f3Smrgstatic int
937ec681f3Smrgnouveau_ioctl_gem_info(int fd, unsigned long request, void *arg)
947ec681f3Smrg{
957ec681f3Smrg   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
967ec681f3Smrg   struct drm_nouveau_gem_info *info = arg;
977ec681f3Smrg   struct nouveau_shim_bo *bo =
987ec681f3Smrg      nouveau_shim_bo(drm_shim_bo_lookup(shim_fd, info->handle));
997ec681f3Smrg   info->map_handle = drm_shim_bo_get_mmap_offset(shim_fd, &bo->base);
1007ec681f3Smrg   info->offset = bo->offset;
1017ec681f3Smrg   info->size = bo->base.size;
1027ec681f3Smrg
1037ec681f3Smrg   drm_shim_bo_put(&bo->base);
1047ec681f3Smrg
1057ec681f3Smrg   return 0;
1067ec681f3Smrg}
1077ec681f3Smrg
1087ec681f3Smrgstatic int
1097ec681f3Smrgnouveau_ioctl_gem_pushbuf(int fd, unsigned long request, void *arg)
1107ec681f3Smrg{
1117ec681f3Smrg   struct drm_nouveau_gem_pushbuf *submit = arg;
1127ec681f3Smrg   submit->vram_available = 3ULL << 30;
1137ec681f3Smrg   submit->gart_available = 1ULL << 40;
1147ec681f3Smrg   return 0;
1157ec681f3Smrg}
1167ec681f3Smrg
1177ec681f3Smrgstatic int
1187ec681f3Smrgnouveau_ioctl_channel_alloc(int fd, unsigned long request, void *arg)
1197ec681f3Smrg{
1207ec681f3Smrg   struct shim_fd *shim_fd = drm_shim_fd_lookup(fd);
1217ec681f3Smrg   struct drm_nouveau_channel_alloc *alloc = arg;
1227ec681f3Smrg   if (device_info.chip_id == 0x50 || device_info.chip_id >= 0x80)
1237ec681f3Smrg      alloc->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART;
1247ec681f3Smrg   else
1257ec681f3Smrg      alloc->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
1267ec681f3Smrg
1277ec681f3Smrg   /* NOTE: this will get leaked since we don't handle the channel
1287ec681f3Smrg    * free. However only one channel is created per screen, so impact should
1297ec681f3Smrg    * be limited. */
1307ec681f3Smrg   struct nouveau_shim_bo *notify = calloc(1, sizeof(*notify));
1317ec681f3Smrg   drm_shim_bo_init(&notify->base, 0x1000);
1327ec681f3Smrg   notify->offset = nouveau.next_offset;
1337ec681f3Smrg   nouveau.next_offset += 0x1000;
1347ec681f3Smrg   alloc->notifier_handle = drm_shim_bo_get_handle(shim_fd, &notify->base);
1357ec681f3Smrg
1367ec681f3Smrg   drm_shim_bo_put(&notify->base);
1377ec681f3Smrg
1387ec681f3Smrg   return 0;
1397ec681f3Smrg}
1407ec681f3Smrg
1417ec681f3Smrgstatic int
1427ec681f3Smrgnouveau_ioctl_get_param(int fd, unsigned long request, void *arg)
1437ec681f3Smrg{
1447ec681f3Smrg   struct drm_nouveau_getparam *gp = arg;
1457ec681f3Smrg
1467ec681f3Smrg   switch (gp->param) {
1477ec681f3Smrg   case NOUVEAU_GETPARAM_CHIPSET_ID:
1487ec681f3Smrg      gp->value = device_info.chip_id;
1497ec681f3Smrg      return 0;
1507ec681f3Smrg   case NOUVEAU_GETPARAM_PCI_VENDOR:
1517ec681f3Smrg      gp->value = 0x10de;
1527ec681f3Smrg      return 0;
1537ec681f3Smrg   case NOUVEAU_GETPARAM_PCI_DEVICE:
1547ec681f3Smrg      gp->value = 0x1004;
1557ec681f3Smrg      return 0;
1567ec681f3Smrg   case NOUVEAU_GETPARAM_BUS_TYPE:
1577ec681f3Smrg      gp->value = 2 /* NV_PCIE */;
1587ec681f3Smrg      return 0;
1597ec681f3Smrg   case NOUVEAU_GETPARAM_FB_SIZE:
1607ec681f3Smrg      gp->value = 3ULL << 30;
1617ec681f3Smrg      return 0;
1627ec681f3Smrg   case NOUVEAU_GETPARAM_AGP_SIZE:
1637ec681f3Smrg      gp->value = 1ULL << 40;
1647ec681f3Smrg      return 0;
1657ec681f3Smrg   case NOUVEAU_GETPARAM_PTIMER_TIME:
1667ec681f3Smrg      gp->value = 0;
1677ec681f3Smrg      return 0;
1687ec681f3Smrg   case NOUVEAU_GETPARAM_HAS_BO_USAGE:
1697ec681f3Smrg      gp->value = 1;
1707ec681f3Smrg      return 0;
1717ec681f3Smrg   case NOUVEAU_GETPARAM_GRAPH_UNITS:
1727ec681f3Smrg      gp->value = 0x01000001;
1737ec681f3Smrg      return 0;
1747ec681f3Smrg   default:
1757ec681f3Smrg      fprintf(stderr, "Unknown DRM_IOCTL_NOUVEAU_GETPARAM %llu\n",
1767ec681f3Smrg              (long long unsigned)gp->param);
1777ec681f3Smrg      return -1;
1787ec681f3Smrg   }
1797ec681f3Smrg}
1807ec681f3Smrg
1817ec681f3Smrgstatic ioctl_fn_t driver_ioctls[] = {
1827ec681f3Smrg   [DRM_NOUVEAU_GETPARAM] = nouveau_ioctl_get_param,
1837ec681f3Smrg   [DRM_NOUVEAU_CHANNEL_ALLOC] = nouveau_ioctl_channel_alloc,
1847ec681f3Smrg   [DRM_NOUVEAU_CHANNEL_FREE] = nouveau_ioctl_noop,
1857ec681f3Smrg   [DRM_NOUVEAU_GROBJ_ALLOC] = nouveau_ioctl_noop,
1867ec681f3Smrg   [DRM_NOUVEAU_NOTIFIEROBJ_ALLOC] = nouveau_ioctl_noop,
1877ec681f3Smrg   [DRM_NOUVEAU_GPUOBJ_FREE] = nouveau_ioctl_noop,
1887ec681f3Smrg   [DRM_NOUVEAU_GEM_NEW] = nouveau_ioctl_gem_new,
1897ec681f3Smrg   [DRM_NOUVEAU_GEM_PUSHBUF] = nouveau_ioctl_gem_pushbuf,
1907ec681f3Smrg   [DRM_NOUVEAU_GEM_CPU_PREP] = nouveau_ioctl_noop,
1917ec681f3Smrg   [DRM_NOUVEAU_GEM_INFO] = nouveau_ioctl_gem_info,
1927ec681f3Smrg};
1937ec681f3Smrg
1947ec681f3Smrgstatic void
1957ec681f3Smrgnouveau_driver_get_device_info(void)
1967ec681f3Smrg{
1977ec681f3Smrg   const char *env = getenv("NOUVEAU_CHIPSET");
1987ec681f3Smrg
1997ec681f3Smrg   if (!env) {
2007ec681f3Smrg      device_info.chip_id = 0xf0;
2017ec681f3Smrg      return;
2027ec681f3Smrg   }
2037ec681f3Smrg
2047ec681f3Smrg   device_info.chip_id = strtol(env, NULL, 16);
2057ec681f3Smrg}
2067ec681f3Smrg
2077ec681f3Smrgvoid
2087ec681f3Smrgdrm_shim_driver_init(void)
2097ec681f3Smrg{
2107ec681f3Smrg   shim_device.bus_type = DRM_BUS_PCI;
2117ec681f3Smrg   shim_device.driver_name = "nouveau";
2127ec681f3Smrg   shim_device.driver_ioctls = driver_ioctls;
2137ec681f3Smrg   shim_device.driver_ioctl_count = ARRAY_SIZE(driver_ioctls);
2147ec681f3Smrg
2157ec681f3Smrg   shim_device.version_major = 1;
2167ec681f3Smrg   shim_device.version_minor = 0;
2177ec681f3Smrg   shim_device.version_patchlevel = 1;
2187ec681f3Smrg
2197ec681f3Smrg   nouveau_driver_get_device_info();
2207ec681f3Smrg
2217ec681f3Smrg   /* nothing looks at the pci id, so fix it to a GTX 780 */
2227ec681f3Smrg   static const char uevent_content[] =
2237ec681f3Smrg      "DRIVER=nouveau\n"
2247ec681f3Smrg      "PCI_CLASS=30000\n"
2257ec681f3Smrg      "PCI_ID=10de:1004\n"
2267ec681f3Smrg      "PCI_SUBSYS_ID=1028:075B\n"
2277ec681f3Smrg      "PCI_SLOT_NAME=0000:01:00.0\n"
2287ec681f3Smrg      "MODALIAS=pci:v000010ded00005916sv00001028sd0000075Bbc03sc00i00\n";
2297ec681f3Smrg   drm_shim_override_file(uevent_content,
2307ec681f3Smrg                          "/sys/dev/char/%d:%d/device/uevent",
2317ec681f3Smrg                          DRM_MAJOR, render_node_minor);
2327ec681f3Smrg   drm_shim_override_file("0x0\n",
2337ec681f3Smrg                          "/sys/dev/char/%d:%d/device/revision",
2347ec681f3Smrg                          DRM_MAJOR, render_node_minor);
2357ec681f3Smrg   drm_shim_override_file("0x10de",
2367ec681f3Smrg                          "/sys/dev/char/%d:%d/device/vendor",
2377ec681f3Smrg                          DRM_MAJOR, render_node_minor);
2387ec681f3Smrg   drm_shim_override_file("0x10de",
2397ec681f3Smrg                          "/sys/devices/pci0000:00/0000:01:00.0/vendor");
2407ec681f3Smrg   drm_shim_override_file("0x1004",
2417ec681f3Smrg                          "/sys/dev/char/%d:%d/device/device",
2427ec681f3Smrg                          DRM_MAJOR, render_node_minor);
2437ec681f3Smrg   drm_shim_override_file("0x1004",
2447ec681f3Smrg                          "/sys/devices/pci0000:00/0000:01:00.0/device");
2457ec681f3Smrg   drm_shim_override_file("0x1234",
2467ec681f3Smrg                          "/sys/dev/char/%d:%d/device/subsystem_vendor",
2477ec681f3Smrg                          DRM_MAJOR, render_node_minor);
2487ec681f3Smrg   drm_shim_override_file("0x1234",
2497ec681f3Smrg                          "/sys/devices/pci0000:00/0000:01:00.0/subsystem_vendor");
2507ec681f3Smrg   drm_shim_override_file("0x1234",
2517ec681f3Smrg                          "/sys/dev/char/%d:%d/device/subsystem_device",
2527ec681f3Smrg                          DRM_MAJOR, render_node_minor);
2537ec681f3Smrg   drm_shim_override_file("0x1234",
2547ec681f3Smrg                          "/sys/devices/pci0000:00/0000:01:00.0/subsystem_device");
2557ec681f3Smrg}
256