1 /*
2 * Copyright © 2024 Collabora Ltd. and Red Hat Inc.
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "nvkmd_nouveau.h"
7
8 #include "nouveau_device.h"
9 #include "util/os_misc.h"
10 #include "vk_log.h"
11
12 #include <fcntl.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <xf86drm.h>
16
17 static bool
drm_device_is_nouveau(const char * path)18 drm_device_is_nouveau(const char *path)
19 {
20 int fd = open(path, O_RDWR | O_CLOEXEC);
21 if (fd < 0)
22 return false;
23
24 drmVersionPtr ver = drmGetVersion(fd);
25 if (!ver) {
26 close(fd);
27 return false;
28 }
29
30 const bool is_nouveau = !strncmp("nouveau", ver->name, ver->name_len);
31
32 drmFreeVersion(ver);
33 close(fd);
34
35 return is_nouveau;
36 }
37
38 VkResult
nvkmd_nouveau_try_create_pdev(struct _drmDevice * drm_device,struct vk_object_base * log_obj,enum nvk_debug debug_flags,struct nvkmd_pdev ** pdev_out)39 nvkmd_nouveau_try_create_pdev(struct _drmDevice *drm_device,
40 struct vk_object_base *log_obj,
41 enum nvk_debug debug_flags,
42 struct nvkmd_pdev **pdev_out)
43 {
44 if (!(drm_device->available_nodes & (1 << DRM_NODE_RENDER)))
45 return VK_ERROR_INCOMPATIBLE_DRIVER;
46
47 switch (drm_device->bustype) {
48 case DRM_BUS_PCI:
49 if (drm_device->deviceinfo.pci->vendor_id != NVIDIA_VENDOR_ID)
50 return VK_ERROR_INCOMPATIBLE_DRIVER;
51 break;
52
53 case DRM_BUS_PLATFORM: {
54 const char *compat_prefix = "nvidia,";
55 bool found = false;
56 for (int i = 0; drm_device->deviceinfo.platform->compatible[i] != NULL; i++) {
57 if (strncmp(drm_device->deviceinfo.platform->compatible[0], compat_prefix, strlen(compat_prefix)) == 0) {
58 found = true;
59 break;
60 }
61 }
62 if (!found)
63 return VK_ERROR_INCOMPATIBLE_DRIVER;
64 break;
65 }
66
67 default:
68 return VK_ERROR_INCOMPATIBLE_DRIVER;
69 }
70
71 if (!drm_device_is_nouveau(drm_device->nodes[DRM_NODE_RENDER]))
72 return VK_ERROR_INCOMPATIBLE_DRIVER;
73
74 struct nouveau_ws_device *ws_dev = nouveau_ws_device_new(drm_device);
75 if (!ws_dev)
76 return vk_error(log_obj, VK_ERROR_INCOMPATIBLE_DRIVER);
77
78 if (!ws_dev->has_vm_bind) {
79 nouveau_ws_device_destroy(ws_dev);
80 return vk_errorf(log_obj, VK_ERROR_INCOMPATIBLE_DRIVER,
81 "NVK Requires a Linux kernel version 6.6 or later");
82 }
83
84 struct stat st;
85 if (stat(drm_device->nodes[DRM_NODE_RENDER], &st)) {
86 nouveau_ws_device_destroy(ws_dev);
87 return vk_errorf(log_obj, VK_ERROR_INITIALIZATION_FAILED,
88 "fstat() failed on %s: %m",
89 drm_device->nodes[DRM_NODE_RENDER]);
90 }
91 const dev_t render_dev = st.st_rdev;
92
93 struct nvkmd_nouveau_pdev *pdev = CALLOC_STRUCT(nvkmd_nouveau_pdev);
94 if (pdev == NULL) {
95 nouveau_ws_device_destroy(ws_dev);
96 return vk_error(log_obj, VK_ERROR_OUT_OF_HOST_MEMORY);
97 }
98
99 pdev->base.ops = &nvkmd_nouveau_pdev_ops;
100 pdev->base.debug_flags = debug_flags;
101 pdev->base.dev_info = ws_dev->info;
102 pdev->base.kmd_info = (struct nvkmd_info) {
103 .has_dma_buf = true,
104 .has_get_vram_used = nouveau_ws_device_vram_used(ws_dev) != 0,
105 .has_alloc_tiled = nouveau_ws_device_has_tiled_bo(ws_dev),
106 .has_map_fixed = true,
107 .has_overmap = true,
108 };
109
110 /* Nouveau uses the OS page size for all pages, regardless of whether they
111 * come from VRAM or system RAM.
112 */
113 uint64_t os_page_size;
114 os_get_page_size(&os_page_size);
115 assert(os_page_size <= UINT32_MAX);
116 pdev->base.bind_align_B = os_page_size;
117
118 pdev->base.drm.render_dev = render_dev;
119
120 /* DRM primary is optional */
121 if ((drm_device->available_nodes & (1 << DRM_NODE_PRIMARY)) &&
122 !stat(drm_device->nodes[DRM_NODE_PRIMARY], &st))
123 pdev->base.drm.primary_dev = st.st_rdev;
124
125 pdev->primary_fd = -1;
126 pdev->ws_dev = ws_dev;
127
128 pdev->syncobj_sync_type = vk_drm_syncobj_get_type(ws_dev->fd);
129 pdev->sync_types[0] = &pdev->syncobj_sync_type;
130 pdev->sync_types[1] = NULL;
131 pdev->base.sync_types = pdev->sync_types;
132
133 *pdev_out = &pdev->base;
134
135 return VK_SUCCESS;
136 }
137
138 static void
nvkmd_nouveau_pdev_destroy(struct nvkmd_pdev * _pdev)139 nvkmd_nouveau_pdev_destroy(struct nvkmd_pdev *_pdev)
140 {
141 struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev);
142
143 nouveau_ws_device_destroy(pdev->ws_dev);
144 FREE(pdev);
145 }
146
147 static uint64_t
nvkmd_nouveau_pdev_get_vram_used(struct nvkmd_pdev * _pdev)148 nvkmd_nouveau_pdev_get_vram_used(struct nvkmd_pdev *_pdev)
149 {
150 struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev);
151
152 return nouveau_ws_device_vram_used(pdev->ws_dev);
153 }
154
155 static int
nvkmd_nouveau_pdev_get_drm_primary_fd(struct nvkmd_pdev * _pdev)156 nvkmd_nouveau_pdev_get_drm_primary_fd(struct nvkmd_pdev *_pdev)
157 {
158 struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev);
159
160 if (pdev->primary_fd >= 0)
161 return pdev->primary_fd;
162
163 if (pdev->base.drm.primary_dev == 0)
164 return -1;
165
166 drmDevicePtr drm_device = NULL;
167 int ret = drmGetDeviceFromDevId(pdev->base.drm.primary_dev, 0, &drm_device);
168 if (ret != 0)
169 return -1;
170
171 int fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
172 drmFreeDevice(&drm_device);
173
174 /* TODO: Test if the FD is usable? */
175
176 pdev->primary_fd = fd;
177
178 return pdev->primary_fd;
179 }
180
181 const struct nvkmd_pdev_ops nvkmd_nouveau_pdev_ops = {
182 .destroy = nvkmd_nouveau_pdev_destroy,
183 .get_vram_used = nvkmd_nouveau_pdev_get_vram_used,
184 .get_drm_primary_fd = nvkmd_nouveau_pdev_get_drm_primary_fd,
185 .create_dev = nvkmd_nouveau_create_dev,
186 };
187