xfree86: make search paths configurable for certain modules

It is patch 1/2 of a series that provides a convenient way to specify
module search paths on a per-driver basis.

This patch allows to set ModulePath for certain modules, in particular,
GPU drivers.

Signed-off-by: Oleh Nykyforchyn <oleh.nyk@gmail.com>
This commit is contained in:
Oleh Nykyforchyn 2025-09-15 18:19:15 +03:00 committed by Enrico Weigelt
parent d66dea68af
commit 9f25f5d3f1
12 changed files with 322 additions and 38 deletions

16
config/10-nvidia.conf.in Normal file
View File

@ -0,0 +1,16 @@
# This xorg.conf.d configuration snippet configures the X server to
# automatically load the nvidia X driver and nvidia GLX libraries
# when it detects a device driven by nvidia-drm.ko kernel module.
# Please note that this works on Linux kernels version 6.12 or higher
# with CONFIG_DRM enabled, and only if the nvidia-drm.ko kernel module
# is loaded before the X server is started.
Section "OutputClass"
Identifier "NVidia"
MatchDriver "nvidia-drm"
Driver "nvidia"
Module "glx"
Module "glxserver_nvidia"
Option "AllowEmptyInitialConfiguration"
ModulePath "@libdir@/nvidia/xorg"
EndSection

View File

@ -26,6 +26,14 @@ endif
if build_xorg
install_data('10-quirks.conf',
install_dir: join_paths(get_option('datadir'), 'X11/xorg.conf.d'))
nvidia_conf = configuration_data()
nvidia_conf.set('libdir', join_paths(get_option('prefix'),get_option('libdir')))
configure_file(
input: '10-nvidia.conf.in',
output: '10-nvidia.conf',
configuration: nvidia_conf,
install_dir: join_paths(get_option('datadir'), 'X11/xorg.conf.d'),
)
endif
libxserver_config = static_library('xserver_config',

View File

@ -329,7 +329,7 @@ InitOutput(int argc, char **argv)
LoaderInit();
/* Tell the loader the default module search path */
LoaderSetPath(xf86ModulePath);
LoaderSetPath(NULL, xf86ModulePath);
if (xf86Info.ignoreABI) {
LoaderSetIgnoreAbi();
@ -749,6 +749,8 @@ CloseInput(void)
{
config_fini();
mieqFini();
/* strictly speaking the below is not related to input, but ... */
LoaderClose();
}
/*

View File

@ -197,9 +197,11 @@ xf86OutputClassDriverList(int index, XF86MatchedDrivers *md)
LogMessageVerb(X_INFO, 1, "Applying OutputClass \"%s\" to %s\n",
cl->identifier, path);
if (cl->driver != NULL && *(cl->driver)) {
LogMessageVerb(X_NONE, 1, "\tloading driver: %s\n", cl->driver);
xf86AddMatchedDriver(md, cl->driver);
} else
LogMessageVerb(X_NONE, 1, "\tno driver specified\n");
}
}
}
@ -261,7 +263,8 @@ xf86platformProbe(void)
Bool pci = TRUE;
XF86ConfOutputClassPtr cl, cl_head = (xf86configptr) ?
xf86configptr->conf_outputclass_lst : NULL;
char *old_path, *path = NULL;
char *driver_path, *path = NULL;
char *curr, *next, *copy;
config_odev_probe(xf86PlatformDeviceProbe);
@ -284,19 +287,85 @@ xf86platformProbe(void)
if (!OutputClassMatches(cl, &xf86_platform_devices[i]))
continue;
if (cl->modulepath && xf86ModPathFrom != X_CMDLINE) {
old_path = path;
if (xf86ModPathFrom != X_CMDLINE) {
if (cl->driver) {
if (cl->modulepath) {
if (*(cl->modulepath)) {
XNFasprintf(&driver_path, "%s,%s", cl->modulepath, xf86ModulePath);
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" ModulePath for driver %s overridden with \"%s\"\n",
cl->identifier, cl->driver, driver_path);
} else {
XNFasprintf(&driver_path, "%s", xf86ModulePath);
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" ModulePath for driver %s reset to standard \"%s\"\n",
cl->identifier, cl->driver, driver_path);
}
} else {
driver_path = NULL;
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" ModulePath for driver %s reset to default\n",
cl->identifier, cl->driver);
}
if (*(cl->driver)) LoaderSetPath(cl->driver, driver_path);
if (cl->modules) {
LogMessageVerb(X_CONFIG, 1, " and for modules \"%s\" as well\n",
cl->modules);
XNFasprintf(&copy, "%s", cl->modules);
curr = copy;
while ((curr = strtok_r(curr, ",", &next))) {
if (*curr) LoaderSetPath(curr, driver_path);
curr = NULL;
}
free(copy);
}
free(driver_path);
}
else if (cl->modules) {
if (cl->modulepath) {
if (*(cl->modulepath)) {
XNFasprintf(&driver_path, "%s,%s", cl->modulepath, xf86ModulePath);
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" ModulePath for modules %s overridden with \"%s\"\n",
cl->identifier, cl->modules, driver_path);
} else {
XNFasprintf(&driver_path, "%s", xf86ModulePath);
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" ModulePath for modules %s reset to standard \"%s\"\n",
cl->identifier, cl->modules, driver_path);
}
} else {
driver_path = NULL;
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" ModulePath for modules %s reset to default\n",
cl->identifier, cl->modules);
}
XNFasprintf(&copy, "%s", cl->modules);
curr = copy;
while ((curr = strtok_r(curr, ",", &next))) {
if (*curr) LoaderSetPath(curr, driver_path);
curr = NULL;
}
free(copy);
} else {
driver_path = path; /* Reuse for temporary storage */
if (*(cl->modulepath)) {
XNFasprintf(&path, "%s,%s", cl->modulepath,
path ? path : xf86ModulePath);
free(old_path);
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" ModulePath extended to \"%s\"\n",
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" default ModulePath extended to \"%s\"\n",
cl->identifier, path);
LoaderSetPath(path);
} else {
XNFasprintf(&path, "%s", xf86ModulePath);
LogMessageVerb(X_CONFIG, 1, "OutputClass \"%s\" default ModulePath reset to standard \"%s\"\n",
cl->identifier, path);
}
}
/* Otherwise global module search path is left unchanged */
}
}
}
if (xf86ModPathFrom != X_CMDLINE) {
if (path) {
LoaderSetPath(NULL, path);
free(path);
} else
LoaderSetPath(NULL, xf86ModulePath);
}
/* First see if there is an OutputClass match marking a device as primary */
for (i = 0; i < xf86_num_platform_devices; i++) {

View File

@ -4779,15 +4779,16 @@ XFree86 common layer.
<blockquote><para>
<programlisting>
void LoaderSetPath(const char *path);
void LoaderSetPath(const char *driver, const char *path);
</programlisting>
<blockquote><para>
The <function>LoaderSetPath()</function> function initialises a default
module search path. This must be called if calls to other functions
are to be made without explicitly specifying a module search path.
The search path <parameter>path</parameter> must be a string of one or more
comma separated absolute paths. Modules are expected to be located
below these paths, possibly in subdirectories of these paths.
The <function>LoaderSetPath()</function> function initialises a search path
for <parameter>driver</parameter> module, if it is non-NULL,
or a default module search path otherwise. This must be called if calls
to other functions are to be made without explicitly specifying a module
search path. The search path <parameter>path</parameter> must be a string
of one or more comma separated absolute paths. Modules are expected to be
located below these paths, possibly in subdirectories of these paths.
</para>
</blockquote></para></blockquote>

View File

@ -87,6 +87,14 @@ LoaderInit(void)
LogMessageVerb(X_NONE, 2, "\t%s : %d.%d\n", ABI_CLASS_EXTENSION,
GET_ABI_MAJOR(LoaderVersionInfo.extensionVersion),
GET_ABI_MINOR(LoaderVersionInfo.extensionVersion));
LoaderInitPath();
}
void
LoaderClose(void)
{
LoaderClosePath();
}
/* Public Interface to the loader. */

View File

@ -69,11 +69,15 @@ typedef struct module_desc {
/* External API for the loader */
void LoaderInit(void);
void LoaderClose(void);
ModuleDescPtr LoadModule(const char *, void *, const XF86ModReqInfo *, int *);
ModuleDescPtr DuplicateModule(ModuleDescPtr mod, ModuleDescPtr parent);
void UnloadDriver(ModuleDescPtr);
void LoaderSetPath(const char *path);
void LoaderSetPath(const char *driver, const char *path);
void LoaderInitPath(void);
void LoaderClosePath(void);
void LoaderUnload(const char *, void *);
unsigned long LoaderGetModuleVersion(ModuleDescPtr mod);

View File

@ -101,6 +101,32 @@ FreeStringList(char **paths)
static char **defaultPathList = NULL;
typedef struct {
struct xorg_list entry;
char *name;
char **paths;
} LoaderModulePathListItem;
struct xorg_list modulePathLists;
void LoaderInitPath(void) {
/* defaultPathList is already set in xf86Init */
xorg_list_init(&modulePathLists);
}
void LoaderClosePath(void) {
LoaderModulePathListItem *item, *next;
xorg_list_for_each_entry_safe(item, next, &modulePathLists, entry) {
xorg_list_del(&item->entry);
free(item->name);
if (item->paths)
FreeStringList(item->paths);
free(item);
}
xorg_list_del(&modulePathLists);
FreeStringList(defaultPathList);
}
static Bool
PathIsAbsolute(const char *path)
{
@ -163,15 +189,73 @@ InitPathList(const char *path)
return list;
}
/*
* Set a default search path or a search path for a specific driver
*/
void
LoaderSetPath(const char *path)
LoaderSetPath(const char *driver, const char *path)
{
if (!path)
return;
LoaderModulePathListItem *item;
if (!driver) {
if (path) {
FreeStringList(defaultPathList);
defaultPathList = InitPathList(path);
}
return;
}
xorg_list_for_each_entry(item, &modulePathLists, entry) {
if (!strcmp(item->name, driver)) {
FreeStringList(item->paths);
if (path)
item->paths = InitPathList(path);
else
item->paths = NULL;
return;
}
}
item = malloc(sizeof(LoaderModulePathListItem));
if (item) {
item->name = strdup(driver);
if (path)
item->paths = InitPathList(path);
else
item->paths = NULL;
}
if (item && item->name && (!path || item->paths))
xorg_list_add(&item->entry, &modulePathLists);
else {
LogMessage(X_ERROR, "Failed to store module search path \"%s\" for module %s\n", path, driver);
if (item) {
if (item->name) free(item->name);
if (item->paths) FreeStringList(item->paths);
free(item);
}
}
}
/*
* Get a default search path or a search path for a specific driver
* and make it effective
*/
static char **
LoaderGetPath(const char *module)
{
LoaderModulePathListItem *item;
xorg_list_for_each_entry(item, &modulePathLists, entry) {
if (!strcmp(item->name, module)) {
if (item->paths)
return item->paths;
else
return defaultPathList;
}
}
return defaultPathList;
}
/* Standard set of module subdirectories to search, in order of preference */
static const char *stdSubdirs[] = {
@ -725,7 +809,7 @@ LoadModule(const char *module, void *options, const XF86ModReqInfo *modreq,
goto LoadModule_fail;
}
pathlist = defaultPathList;
pathlist = LoaderGetPath(name);
if (!pathlist) {
/* This could be a calloc failure too */
if (errmaj)

View File

@ -435,7 +435,7 @@ searches for loadable modules loading in the order specified.
Multiple
.B ModulePath
entries may be specified, and they will be concatenated to build the
module search path used by the server. The default module path is
module search path used by the server. The standard default module path is
.PP
.RS 11
@modulepath@
@ -1327,11 +1327,11 @@ When an output device has been matched to the
.B OutputClass
section, any
.B Option
entries are applied to the device. One
entries are applied to the device. Two
.B OutputClass
specific
specific kinds of
.B Option
is recognized. See the
are recognized. See the
.B Device
section below for a description of the remaining
.B Option
@ -1356,21 +1356,90 @@ If multiple output devices match an
section with the
.B HotplugDriver
option, the first one enumerated becomes the hotplug driver.
A
.RE
.PP
.TP 7
An
.B OutputClass
Section may contain
.B ModulePath
entries. When an output device matches an
section may contain multiple
.TP 7
.BI "ModulePath \*q" path \*q
entries. When an output device with DRM enabled matches an
.B OutputClass
section, any
section and
.B Driver
.I \*qdriver\*q
is set, then for this driver module, and all modules loaded until the next driver
for a DRM-enabled GPU, a search path is created by prepending all
.B ModulePath
entries in that
.B OutputClass
are pre-pended to the search path for loadable Xorg server modules. See
to the
.I standard
search path \(lq@modulepath@\(rq for loadable Xorg server modules (not the
.I effective
default set path, which can undergo changes, see below). If there is a single
.PP
.RS 11
.nf
.B " ModulePath \*q\*q
.fi
.RE
.PP
.RS 7
entry in an
.B OutputClass
for a
.I driver
, then only the
.I standard
path \(lq@modulepath@\(rq is searched from now on. It there no
.B ModulePath
entries at all, then the
.I effective
default search path (set globally) becomes active. Note that each
.B OutputClass
.I overrides
search path for a
.I driver
, not
.I extends
it.
.PP
If a matching
.B OutputClass
contains
.B ModulePath
entries, but not a
.B Driver
entry, then paths are
.I prepended
to the
.I effective
default search path, which initially is standard \(lq@modulepath@\(rq, thus extending it.
.PP
These entries have no effect if a search path for loadable modules is set through command line. See
.B ModulePath
in the
.B Files
section for more info.
.RE
.PP
.B OutputClass
section may contain an arbitrary number of
.PP
.RS 4
.nf
.BI " Module \*q" module\*q
.fi
.RE
.PP
entries, which are quite equivalent to
.B Driver
except that
.I \*qmodule\*q
is not added to the list of autoconfigured video drivers. This may be useful to extend
ABI version ignoring and special search paths to modules like "glx".
.SH "DEVICE SECTION"
The config file may have multiple
.B Device

View File

@ -38,6 +38,7 @@ static const xf86ConfigSymTabRec OutputClassTab[] = {
{ENDSECTION, "endsection"},
{IDENTIFIER, "identifier"},
{DRIVER, "driver"},
{MODULE, "module"},
{MODULEPATH, "modulepath"},
{OPTION, "option"},
{MATCH_DRIVER, "matchdriver"},
@ -53,6 +54,7 @@ xf86freeOutputClassList(XF86ConfOutputClassPtr ptr)
TestFree(ptr->identifier);
TestFree(ptr->comment);
TestFree(ptr->driver);
TestFree(ptr->modules);
TestFree(ptr->modulepath);
xf86freeMatchGroupList(&ptr->match_driver);
@ -99,6 +101,19 @@ xf86parseOutputClassSection(void)
else
ptr->driver = xf86_lex_val.str;
break;
case MODULE:
if (xf86getSubToken(&(ptr->comment)) != XF86_TOKEN_STRING)
Error(QUOTE_MSG, "Module");
if (ptr->modules) {
char *path;
XNFasprintf(&path, "%s,%s", ptr->modules, xf86_lex_val.str);
free(xf86_lex_val.str);
free(ptr->modules);
ptr->modules = path;
} else {
ptr->modules = xf86_lex_val.str;
}
break;
case MODULEPATH:
if (xf86getSubToken(&(ptr->comment)) != XF86_TOKEN_STRING)
Error(QUOTE_MSG, "ModulePath");
@ -159,6 +174,10 @@ xf86printOutputClassSection(FILE * cf, XF86ConfOutputClassPtr ptr)
fprintf(cf, "\tIdentifier \"%s\"\n", ptr->identifier);
if (ptr->driver)
fprintf(cf, "\tDriver \"%s\"\n", ptr->driver);
if (ptr->modules)
fprintf(cf, "\tModule \"%s\"\n", ptr->modules);
if (ptr->modulepath)
fprintf(cf, "\tModulePath \"%s\"\n", ptr->modulepath);
xorg_list_for_each_entry(group, &ptr->match_driver, entry) {
fprintf(cf, "\tMatchDriver \"");

View File

@ -359,6 +359,7 @@ typedef struct {
GenericListRec list;
char *identifier;
char *driver;
char *modules;
char *modulepath;
struct xorg_list match_driver;
XF86OptionPtr option_lst;

View File

@ -255,6 +255,9 @@ typedef enum {
NOMATCH_DRIVER,
NOMATCH_TAG,
NOMATCH_LAYOUT,
/* OutputClass Tokens */
MODULE,
} ParserTokens;
#endif /* _xf86_tokens_h */