1/************************************************************************** 2 * 3 * Copyright 2011 Intel Corporation 4 * Copyright 2012 Francisco Jerez 5 * All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the 9 * "Software"), to deal in the Software without restriction, including 10 * without limitation the rights to use, copy, modify, merge, publish, 11 * distribute, sub license, and/or sell copies of the Software, and to 12 * permit persons to whom the Software is furnished to do so, subject to 13 * the following conditions: 14 * 15 * The above copyright notice and this permission notice (including the 16 * next paragraph) shall be included in all copies or substantial portions 17 * of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Authors: 28 * Kristian Høgsberg <krh@bitplanet.net> 29 * Benjamin Franzke <benjaminfranzke@googlemail.com> 30 * 31 **************************************************************************/ 32 33#include <fcntl.h> 34#include <stdio.h> 35#include <string.h> 36#include <xf86drm.h> 37#include <unistd.h> 38#include <fcntl.h> 39 40#include "loader.h" 41#include "target-helpers/drm_helper_public.h" 42#include "frontend/drm_driver.h" 43#include "pipe_loader_priv.h" 44 45#include "util/os_file.h" 46#include "util/u_memory.h" 47#include "util/u_dl.h" 48#include "util/u_debug.h" 49#include "util/xmlconfig.h" 50 51#define DRM_RENDER_NODE_DEV_NAME_FORMAT "%s/renderD%d" 52#define DRM_RENDER_NODE_MAX_NODES 63 53#define DRM_RENDER_NODE_MIN_MINOR 128 54#define DRM_RENDER_NODE_MAX_MINOR (DRM_RENDER_NODE_MIN_MINOR + DRM_RENDER_NODE_MAX_NODES) 55 56struct pipe_loader_drm_device { 57 struct pipe_loader_device base; 58 const struct drm_driver_descriptor *dd; 59#ifndef GALLIUM_STATIC_TARGETS 60 struct util_dl_library *lib; 61#endif 62 int fd; 63}; 64 65#define pipe_loader_drm_device(dev) ((struct pipe_loader_drm_device *)dev) 66 67static const struct pipe_loader_ops pipe_loader_drm_ops; 68 69#ifdef GALLIUM_STATIC_TARGETS 70static const struct drm_driver_descriptor *driver_descriptors[] = { 71 &i915_driver_descriptor, 72 &iris_driver_descriptor, 73 &crocus_driver_descriptor, 74 &nouveau_driver_descriptor, 75 &r300_driver_descriptor, 76 &r600_driver_descriptor, 77 &radeonsi_driver_descriptor, 78 &vmwgfx_driver_descriptor, 79 &kgsl_driver_descriptor, 80 &msm_driver_descriptor, 81 &virtio_gpu_driver_descriptor, 82 &v3d_driver_descriptor, 83 &vc4_driver_descriptor, 84 &panfrost_driver_descriptor, 85 &etnaviv_driver_descriptor, 86 &tegra_driver_descriptor, 87 &lima_driver_descriptor, 88 &zink_driver_descriptor, 89}; 90#endif 91 92static const struct drm_driver_descriptor * 93get_driver_descriptor(const char *driver_name, struct util_dl_library **plib) 94{ 95#ifdef GALLIUM_STATIC_TARGETS 96 for (int i = 0; i < ARRAY_SIZE(driver_descriptors); i++) { 97 if (strcmp(driver_descriptors[i]->driver_name, driver_name) == 0) 98 return driver_descriptors[i]; 99 } 100 return &kmsro_driver_descriptor; 101#else 102 const char *search_dir = getenv("GALLIUM_PIPE_SEARCH_DIR"); 103 if (search_dir == NULL) 104 search_dir = PIPE_SEARCH_DIR; 105 106 *plib = pipe_loader_find_module(driver_name, search_dir); 107 if (!*plib) 108 return NULL; 109 110 const struct drm_driver_descriptor *dd = 111 (const struct drm_driver_descriptor *) 112 util_dl_get_proc_address(*plib, "driver_descriptor"); 113 114 /* sanity check on the driver name */ 115 if (dd && strcmp(dd->driver_name, driver_name) == 0) 116 return dd; 117#endif 118 119 return NULL; 120} 121 122static bool 123pipe_loader_drm_probe_fd_nodup(struct pipe_loader_device **dev, int fd) 124{ 125 struct pipe_loader_drm_device *ddev = CALLOC_STRUCT(pipe_loader_drm_device); 126 int vendor_id, chip_id; 127 128 if (!ddev) 129 return false; 130 131 if (loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) { 132 ddev->base.type = PIPE_LOADER_DEVICE_PCI; 133 ddev->base.u.pci.vendor_id = vendor_id; 134 ddev->base.u.pci.chip_id = chip_id; 135 } else { 136 ddev->base.type = PIPE_LOADER_DEVICE_PLATFORM; 137 } 138 ddev->base.ops = &pipe_loader_drm_ops; 139 ddev->fd = fd; 140 141 ddev->base.driver_name = loader_get_driver_for_fd(fd); 142 if (!ddev->base.driver_name) 143 goto fail; 144 145 /* For the closed source AMD OpenGL driver, we want libgbm to load 146 * "amdgpu_dri.so", but we want Gallium multimedia drivers to load 147 * "radeonsi". So change amdgpu to radeonsi for Gallium. 148 */ 149 if (strcmp(ddev->base.driver_name, "amdgpu") == 0) { 150 FREE(ddev->base.driver_name); 151 ddev->base.driver_name = strdup("radeonsi"); 152 } 153 154 struct util_dl_library **plib = NULL; 155#ifndef GALLIUM_STATIC_TARGETS 156 plib = &ddev->lib; 157#endif 158 ddev->dd = get_driver_descriptor(ddev->base.driver_name, plib); 159 160 /* vgem is a virtual device; don't try using it with kmsro */ 161 if (strcmp(ddev->base.driver_name, "vgem") == 0) 162 goto fail; 163 164 /* kmsro supports lots of drivers, try as a fallback */ 165 if (!ddev->dd) 166 ddev->dd = get_driver_descriptor("kmsro", plib); 167 168 if (!ddev->dd) 169 goto fail; 170 171 *dev = &ddev->base; 172 return true; 173 174 fail: 175#ifndef GALLIUM_STATIC_TARGETS 176 if (ddev->lib) 177 util_dl_close(ddev->lib); 178#endif 179 FREE(ddev->base.driver_name); 180 FREE(ddev); 181 return false; 182} 183 184bool 185pipe_loader_drm_probe_fd(struct pipe_loader_device **dev, int fd) 186{ 187 bool ret; 188 int new_fd; 189 190 if (fd < 0 || (new_fd = os_dupfd_cloexec(fd)) < 0) 191 return false; 192 193 ret = pipe_loader_drm_probe_fd_nodup(dev, new_fd); 194 if (!ret) 195 close(new_fd); 196 197 return ret; 198} 199 200static int 201open_drm_render_node_minor(int minor) 202{ 203 char path[PATH_MAX]; 204 snprintf(path, sizeof(path), DRM_RENDER_NODE_DEV_NAME_FORMAT, DRM_DIR_NAME, 205 minor); 206 return loader_open_device(path); 207} 208 209int 210pipe_loader_drm_probe(struct pipe_loader_device **devs, int ndev) 211{ 212 int i, j, fd; 213 214 for (i = DRM_RENDER_NODE_MIN_MINOR, j = 0; 215 i <= DRM_RENDER_NODE_MAX_MINOR; i++) { 216 struct pipe_loader_device *dev; 217 218 fd = open_drm_render_node_minor(i); 219 if (fd < 0) 220 continue; 221 222 if (!pipe_loader_drm_probe_fd_nodup(&dev, fd)) { 223 close(fd); 224 continue; 225 } 226 227 if (j < ndev) { 228 devs[j] = dev; 229 } else { 230 close(fd); 231 dev->ops->release(&dev); 232 } 233 j++; 234 } 235 236 return j; 237} 238 239static void 240pipe_loader_drm_release(struct pipe_loader_device **dev) 241{ 242 struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(*dev); 243 244#ifndef GALLIUM_STATIC_TARGETS 245 if (ddev->lib) 246 util_dl_close(ddev->lib); 247#endif 248 249 close(ddev->fd); 250 FREE(ddev->base.driver_name); 251 pipe_loader_base_release(dev); 252} 253 254static const struct driOptionDescription * 255pipe_loader_drm_get_driconf(struct pipe_loader_device *dev, unsigned *count) 256{ 257 struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(dev); 258 259 *count = ddev->dd->driconf_count; 260 return ddev->dd->driconf; 261} 262 263static struct pipe_screen * 264pipe_loader_drm_create_screen(struct pipe_loader_device *dev, 265 const struct pipe_screen_config *config, bool sw_vk) 266{ 267 struct pipe_loader_drm_device *ddev = pipe_loader_drm_device(dev); 268 269 return ddev->dd->create_screen(ddev->fd, config); 270} 271 272const struct driOptionDescription * 273pipe_loader_drm_get_driconf_by_name(const char *driver_name, unsigned *count) 274{ 275 driOptionDescription *driconf = NULL; 276 struct util_dl_library *lib = NULL; 277 const struct drm_driver_descriptor *dd = 278 get_driver_descriptor(driver_name, &lib); 279 280 if (!dd) { 281 *count = 0; 282 } else { 283 *count = dd->driconf_count; 284 size_t size = sizeof(*driconf) * *count; 285 driconf = malloc(size); 286 memcpy(driconf, dd->driconf, size); 287 } 288 if (lib) 289 util_dl_close(lib); 290 291 return driconf; 292} 293 294static const struct pipe_loader_ops pipe_loader_drm_ops = { 295 .create_screen = pipe_loader_drm_create_screen, 296 .get_driconf = pipe_loader_drm_get_driconf, 297 .release = pipe_loader_drm_release 298}; 299