diff --git a/src/gbm/main/backend.c b/src/gbm/main/backend.c index b002df92e16..b628c403faa 100644 --- a/src/gbm/main/backend.c +++ b/src/gbm/main/backend.c @@ -1,5 +1,6 @@ /* * Copyright © 2011 Intel Corporation + * Copyright © 2021 NVIDIA Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -23,6 +24,7 @@ * * Authors: * Benjamin Franzke + * James Jones */ #include @@ -31,7 +33,9 @@ #include #include #include +#include +#include "loader.h" #include "backend.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) @@ -42,12 +46,51 @@ extern const struct gbm_backend gbm_dri_backend; struct gbm_backend_desc { const char *name; const struct gbm_backend *backend; + void *lib; }; static const struct gbm_backend_desc builtin_backends[] = { { "dri", &gbm_dri_backend }, }; +static const char *backend_search_path_vars[] = { + "GBM_BACKENDS_PATH", + NULL +}; + +static void +free_backend_desc(const struct gbm_backend_desc *backend_desc) +{ + assert(backend_desc->lib); + + dlclose(backend_desc->lib); + free((void *)backend_desc->name); + free((void *)backend_desc); +} + +static struct gbm_backend_desc * +create_backend_desc(const char *name, + const struct gbm_backend *backend, + void *lib) +{ + struct gbm_backend_desc *new_desc = calloc(1, sizeof(*new_desc)); + + if (!new_desc) + return NULL; + + new_desc->name = strdup(name); + + if (!new_desc->name) { + free(new_desc); + return NULL; + } + + new_desc->backend = backend; + new_desc->lib = lib; + + return new_desc; +} + static struct gbm_device * backend_create_device(const struct gbm_backend_desc *bd, int fd) { @@ -56,18 +99,53 @@ backend_create_device(const struct gbm_backend_desc *bd, int fd) struct gbm_device *dev = bd->backend->v0.create_device(fd, abi_ver); if (dev) { - assert(abi_ver == dev->v0.backend_version); + if (abi_ver != dev->v0.backend_version) { + _gbm_device_destroy(dev); + return NULL; + } dev->v0.backend_desc = bd; } return dev; } +static struct gbm_device * +load_backend(void *lib, int fd, const char *name) +{ + struct gbm_device *dev = NULL; + struct gbm_backend_desc *backend_desc; + const struct gbm_backend *gbm_backend; + GBM_GET_BACKEND_PROC_PTR get_backend; + + get_backend = dlsym(lib, GBM_GET_BACKEND_PROC_NAME); + + if (!get_backend) + goto fail; + + gbm_backend = get_backend(&gbm_core); + backend_desc = create_backend_desc(name, gbm_backend, lib); + + if (!backend_desc) + goto fail; + + dev = backend_create_device(backend_desc, fd); + + if (!dev) + free_backend_desc(backend_desc); + + return dev; + +fail: + dlclose(lib); + return NULL; +} + static struct gbm_device * find_backend(const char *name, int fd) { struct gbm_device *dev = NULL; const struct gbm_backend_desc *bd; + void *lib; unsigned i; for (i = 0; i < ARRAY_SIZE(builtin_backends); ++i) { @@ -82,6 +160,16 @@ find_backend(const char *name, int fd) break; } + if (name && !dev) { + lib = loader_open_driver_lib(name, "_gbm", + backend_search_path_vars, + DEFAULT_BACKENDS_PATH, + true); + + if (lib) + dev = load_backend(lib, fd, name); + } + return dev; } @@ -114,5 +202,9 @@ _gbm_create_device(int fd) void _gbm_device_destroy(struct gbm_device *gbm) { + const struct gbm_backend_desc *backend_desc = gbm->v0.backend_desc; gbm->v0.destroy(gbm); + + if (backend_desc && backend_desc->lib) + free_backend_desc(backend_desc); } diff --git a/src/gbm/main/gbm_abi_check.c b/src/gbm/main/gbm_abi_check.c index 590599a095f..f1137be7baf 100644 --- a/src/gbm/main/gbm_abi_check.c +++ b/src/gbm/main/gbm_abi_check.c @@ -216,6 +216,8 @@ struct gbm_core_abi0 { struct gbm_core_v0_abi0 v0; }; +typedef const struct gbm_backend *(*GBM_GET_BACKEND_PROC_PTR_abi0)(const struct gbm_core *gbm_core); + /* * Structure/member ABI-checking helper macros */ @@ -310,6 +312,17 @@ struct gbm_core_abi0 { } \ } while (0) +#define CHECK_PROC(proc, a_ver, b_ver) \ + do { \ + proc ## a_ver a; \ + proc ## b_ver b = NULL; \ + a = b; \ + (void)a; \ + } while (0) + +#define CHECK_PROC_CURRENT(proc, a_ver) \ + CHECK_PROC(proc, a_ver,) + int main(int argc, char **argv) { /********************************************/ @@ -401,5 +414,8 @@ int main(int argc, char **argv) /* Size of ABI-versioned substructures verified by above member checks */ CHECK_SIZE_CURRENT (gbm_core, _abi0); + + CHECK_PROC_CURRENT (GBM_GET_BACKEND_PROC_PTR, _abi0); + return 0; } diff --git a/src/gbm/main/gbm_backend_abi.h b/src/gbm/main/gbm_backend_abi.h index 1bf73b75120..962ee74f003 100644 --- a/src/gbm/main/gbm_backend_abi.h +++ b/src/gbm/main/gbm_backend_abi.h @@ -278,4 +278,20 @@ struct gbm_core { struct gbm_core_v0 v0; }; +/** + * The entrypoint an external GBM backend exports. + * + * Prior to creating any devices using the backend, GBM will look up and call + * this function to request the backend's interface and convey the loader's + * version and exported interface to the backend. + * + * DO NOT MODIFY THIS FUNCTION NAME OR PROTOTYPE. It must remain unchanged to + * preserve backwards compatibility with existing GBM backends. + */ +#define GBM_GET_BACKEND_PROC gbmint_get_backend +#define _GBM_MKSTRX(s) _GBM_MKSTR(s) +#define _GBM_MKSTR(s) #s +#define GBM_GET_BACKEND_PROC_NAME _GBM_MKSTRX(GBM_GET_BACKEND_PROC) +typedef const struct gbm_backend *(*GBM_GET_BACKEND_PROC_PTR)(const struct gbm_core *gbm_core); + #endif