From: John Wolfe Date: Thu, 4 Feb 2021 17:57:03 +0000 (-0800) Subject: resolution: Fix kms autodetection X-Git-Tag: stable-11.3.0~156 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ff5eb5f448c78448b96f9c3db957d19f0288a9b6;p=thirdparty%2Fopen-vm-tools.git resolution: Fix kms autodetection Currently, the elf binary of the xorg driver is loaded to check for a string that was put in the .modinfo section in the driver. Unfortunately there are two problems with this approach: 1) Distros now ship without xorg, so the xorg .so doesn't exist and there's nothing to check. 2) Distros (e.g. Fedora) do heavy optimizations and remove the .modinfo section from the .so, so the string cannot be found even though the driver exists. To fix both, stop depending on being able to parse the elf binary of the xorg driver. Instead, let the plugin check for the existence of the drm driver with a sufficiently high version, and if it exists, use kms. This removes the dependency on X for kms. Also increase the version of vmwgfx required to ensure atomic mode-setting plus relevant bug fixes (in the kernel since 2017) are available. --- diff --git a/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.c b/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.c index 0f40639c7..1b5c989f2 100644 --- a/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.c +++ b/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.c @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2016-2017,2020 VMware, Inc. All rights reserved. + * Copyright (C) 2016-2021 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -42,25 +42,8 @@ /* Required DRM version for resolutionKMS */ #define RESOLUTION_DRM_MAJOR 2 -#define RESOLUTION_DRM_MINOR 12 +#define RESOLUTION_DRM_MINOR 14 -/* Required Xorg driver version for resolutionKMS default on */ -#define RESOLUTION_XORG_MAJOR 13 -#define RESOLUTION_XORG_MINOR 2 - -/* Recognition token for Xorg driver version scanner */ -#define RESOLUTION_XORG_VERSTRING "version=" - -/* - * Xorg driver file names to scan for. Only the first found will be - * scanned for version info. - */ -static const char *driverNames[]= { - "/usr/lib64/xorg/modules/drivers/vmware_drv.so", - "/usr/lib/xorg/modules/drivers/vmware_drv.so" -}; - -static const int numDriverNames = 2; /* *----------------------------------------------------------------------------- @@ -106,52 +89,52 @@ resolutionOpenDRM(const char *node) // IN: Device node base name. */ enumerate = udev_enumerate_new(udev); if (udev_enumerate_add_match_subsystem(enumerate, "drm")) - goto outErr; + goto outErr; if (udev_enumerate_add_match_property(enumerate, "DEVTYPE", "drm_minor")) - goto outErr; + goto outErr; if (udev_enumerate_scan_devices(enumerate)) - goto outErr; + goto outErr; devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(devListEntry, devices) { - const char *path, *vendor, *device; - struct udev_device *parent; - - path = udev_list_entry_get_name(devListEntry); - if (!path) - continue; - if (!strstr(path, node)) - continue; - - dev = udev_device_new_from_syspath(udev, path); - if (!dev) - goto outErr; - - parent = udev_device_get_parent_with_subsystem_devtype(dev, - "pci", - NULL); - if (!parent) - goto skipCheck; - - vendor = udev_device_get_sysattr_value(parent, "vendor"); - device = udev_device_get_sysattr_value(parent, "device"); - if (!vendor || !device) - goto skipCheck; - - if (strcmp(vendor, RESOLUTION_VENDOR) || - strcmp(device, RESOLUTION_DEVICE)) - goto skipCheck; - - devNode = udev_device_get_devnode(dev); - if (!devNode) - goto outFound; - - fd = open(devNode, O_RDWR); - udev_device_unref(dev); - break; + const char *path, *vendor, *device; + struct udev_device *parent; + + path = udev_list_entry_get_name(devListEntry); + if (!path) + continue; + if (!strstr(path, node)) + continue; + + dev = udev_device_new_from_syspath(udev, path); + if (!dev) + goto outErr; + + parent = udev_device_get_parent_with_subsystem_devtype(dev, + "pci", + NULL); + if (!parent) + goto skipCheck; + + vendor = udev_device_get_sysattr_value(parent, "vendor"); + device = udev_device_get_sysattr_value(parent, "device"); + if (!vendor || !device) + goto skipCheck; + + if (strcmp(vendor, RESOLUTION_VENDOR) || + strcmp(device, RESOLUTION_DEVICE)) + goto skipCheck; + + devNode = udev_device_get_devnode(dev); + if (!devNode) + goto outFound; + + fd = open(devNode, O_RDWR); + udev_device_unref(dev); + break; skipCheck: - udev_device_unref(dev); + udev_device_unref(dev); } udev_enumerate_unref(enumerate); @@ -199,23 +182,25 @@ skipCheck: static int resolutionDRMCheckVersion(int fd) // IN: An open DRM file descriptor. { - drmVersionPtr ver = drmGetVersion(fd); + drmVersionPtr ver = drmGetVersion(fd); - if (!ver) { - g_debug("%s: Failed to get DRM version.\n", __func__); - return -1; - } + if (!ver) { + g_debug("%s: Failed to get DRM version.\n", __func__); + return -1; + } - if (ver->version_major != RESOLUTION_DRM_MAJOR || - ver->version_minor < RESOLUTION_DRM_MINOR) { - g_debug("%s: Insufficient DRM version %d.%d for resolutionKMS.\n", - __func__, ver->version_major, ver->version_minor); - drmFreeVersion(ver); - return -1; - } + if (ver->version_major != RESOLUTION_DRM_MAJOR || + ver->version_minor < RESOLUTION_DRM_MINOR) { + g_debug("%s: Insufficient DRM version %d.%d for resolutionKMS.\n", + __func__, ver->version_major, ver->version_minor); + drmFreeVersion(ver); + return -1; + } + g_debug("%s: DRM version %d.%d.\n", + __func__, ver->version_major, ver->version_minor); - drmFreeVersion(ver); - return 0; + drmFreeVersion(ver); + return 0; } @@ -241,27 +226,27 @@ resolutionDRMCheckVersion(int fd) // IN: An open DRM file descriptor. static int resolutionDRMRPrimaryCheckOpen(void) { - int fd = -1; - - fd = resolutionOpenDRM("renderD"); - if (fd < 0) { - g_debug("%s: Failed to open DRM render node.\n", __func__); - fd = resolutionOpenDRM("card"); - if (fd >= 0) - (void) drmDropMaster(fd); - } - if (fd < 0) { - g_debug("%s: Failed to open DRM card node.\n", __func__); - goto outErr; - } + int fd = -1; + + fd = resolutionOpenDRM("renderD"); + if (fd < 0) { + g_debug("%s: Failed to open DRM render node.\n", __func__); + fd = resolutionOpenDRM("card"); + if (fd >= 0) + (void) drmDropMaster(fd); + } + if (fd < 0) { + g_debug("%s: Failed to open DRM card node.\n", __func__); + goto outErr; + } - if (!resolutionDRMCheckVersion(fd)) { - return fd; - } + if (!resolutionDRMCheckVersion(fd)) { + return fd; + } - close(fd); - outErr: - return -1; + close(fd); +outErr: + return -1; } @@ -288,60 +273,43 @@ int resolutionCheckForKMS(ToolsAppCtx *ctx) // IN: The ToolsAppCtx for // configuration db access. { - GError *err = NULL; - gboolean doResolutionKMS; - int fd; - - doResolutionKMS = g_key_file_get_boolean(ctx->config, "resolutionKMS", - "enable", &err); - if (err) { - /* - * If there is nothing in the configuration file, require - * at least Xorg driver version 13.2.0, which has the autolayout - * feature, to enable resolutionKMS. - */ - int major, minor, level; - g_clear_error(&err); - doResolutionKMS = FALSE; - if (!resolutionXorgDriverVersion(numDriverNames, driverNames, - RESOLUTION_XORG_VERSTRING, &major, - &minor, &level) && - (major > RESOLUTION_XORG_MAJOR || - (major == RESOLUTION_XORG_MAJOR && - minor >= RESOLUTION_XORG_MINOR))) { - doResolutionKMS = TRUE; - g_debug("%s: ResolutionKMS enabled based on Xorg driver version.\n", - __func__); - } else { - g_debug("%s: ResolutionKMS disabled. (No configuration).\n", - __func__); - doResolutionKMS = FALSE; - } - } else { - g_debug("%s: ResolutionKMS %s using configuration file info.\n", - __func__, (doResolutionKMS) ? "enabled" : "disabled"); - } + GError *err = NULL; + gboolean doResolutionKMS; + int fd; + + doResolutionKMS = g_key_file_get_boolean(ctx->config, "resolutionKMS", + "enable", &err); + if (err) { + g_clear_error(&err); + /* + * We are going to try to see if we can load a valid vmwgfx + */ + doResolutionKMS = TRUE; + } else { + g_debug("%s: ResolutionKMS %s using configuration file info.\n", + __func__, (doResolutionKMS) ? "enabled" : "disabled"); + } - if (!doResolutionKMS) - return -1; + if (!doResolutionKMS) + return -1; - if (resolutionDLOpen()) { - g_warning("%s: Failed to find needed system libraries for " - "resolutionKMS.\n", __func__); - return -1; - } else { - g_message("%s: dlopen succeeded.\n", __func__); - } + if (resolutionDLOpen()) { + g_warning("%s: Failed to find needed system libraries for " + "resolutionKMS.\n", __func__); + return -1; + } else { + g_message("%s: dlopen succeeded.\n", __func__); + } - fd = resolutionDRMRPrimaryCheckOpen(); + fd = resolutionDRMRPrimaryCheckOpen(); - if (fd < 0) - g_warning("%s: No system support for resolutionKMS.\n", __func__); - else - g_message("%s: System support available for resolutionKMS.\n", - __func__); + if (fd < 0) + g_warning("%s: No system support for resolutionKMS.\n", __func__); + else + g_message("%s: System support available for resolutionKMS.\n", + __func__); - return fd; + return fd; } /* @@ -364,84 +332,8 @@ resolutionCheckForKMS(ToolsAppCtx *ctx) // IN: The ToolsAppCtx for void resolutionDRMClose(int fd) { - close(fd); - resolutionDLClose(); + close(fd); + resolutionDLClose(); } #endif /* ENABLE_RESOLUTIONKMS */ - -/* - *----------------------------------------------------------------------------- - * - * resolutionXorgDriverVersion -- - * - * Scans for VMWare Xorg driver files and tries to determine the Xorg - * driver version. - * - * Results: - * If succesful returns zero and outputs the driver version in the - * parameters major, minor and level. If not successful, returns -1. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ -int -resolutionXorgDriverVersion(int numPaths, // IN: Number of strings - // in paths. - const char *paths[], // IN: Possible driver - // paths. - const char versionString[], // IN: Version token. - int *major, // OUT: Major version # - int *minor, // OUT: Minor version # - int *level) // OUT: Patchlevel - // version # -{ - FILE *driver = NULL; - const char *curMatch; - int curFileChar; - int i; - - g_debug("%s: Scanning for VMWare Xorg drivers.\n", __func__); - for(i = 0; i < numPaths; ++i) { - g_debug("%s: Looking for \"%s\".\n", __func__, paths[i]); - driver = fopen(paths[i], "r"); - if (driver) - break; - } - - if (!driver) { - g_debug("%s: No driver found.\n", __func__); - return -1; - } - - g_debug("%s: Driver found. Looking for version info.\n", __func__); - curMatch = versionString; - while (*curMatch) { - if (feof(driver)) - goto outNotFound; - - curFileChar = fgetc(driver); - if (curFileChar != EOF && curFileChar == *curMatch) { - curMatch++; - continue; - } else if (curMatch != versionString) { - curMatch = versionString; - (void) ungetc(curFileChar, driver); - } - } - - if (fscanf(driver, "%d.%d.%d", major, minor, level) != 3) - goto outNotFound; - - fclose(driver); - g_debug("%s: Version info found: %d.%d.%d\n", __func__, *major, *minor, - *level); - return 0; - - outNotFound: - fclose(driver); - g_debug("%s: No version info found.\n", __func__); - return -1; -} diff --git a/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.h b/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.h index 6565190f8..001383e7e 100644 --- a/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.h +++ b/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.h @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2016 VMware, Inc. All rights reserved. + * Copyright (C) 2016-2021 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -42,12 +42,4 @@ static inline void resolutionDRMClose(int fd) {} #endif /* !ENABLE_RESOLUTIONKMS */ -int -resolutionXorgDriverVersion(int numPaths, - const char *paths[], - const char versionString[], - int *major, - int *minor, - int *level); - #endif /* _RESOLUTION_COMMON_H_ */ diff --git a/open-vm-tools/services/plugins/resolutionSet/resolutionDL.c b/open-vm-tools/services/plugins/resolutionSet/resolutionDL.c index 571ab39c6..09cd460a8 100644 --- a/open-vm-tools/services/plugins/resolutionSet/resolutionDL.c +++ b/open-vm-tools/services/plugins/resolutionSet/resolutionDL.c @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2016 VMware, Inc. All rights reserved. + * Copyright (C) 2016-2021 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -34,44 +34,44 @@ struct FuncToResolv { - size_t offset; - const char *name; + size_t offset; + const char *name; }; #define UDEV_RESOLV(_name) \ - {.offset = offsetof(struct Udev1Interface, _name), \ - .name = "udev"#_name} + {.offset = offsetof(struct Udev1Interface, _name), \ + .name = "udev"#_name} #define LIBDRM_RESOLV(_name) \ - {.offset = offsetof(struct Drm2Interface, _name), \ - .name = "drm"#_name} + {.offset = offsetof(struct Drm2Interface, _name), \ + .name = "drm"#_name} static struct FuncToResolv udev1Table[] = { - UDEV_RESOLV(_device_get_devnode), - UDEV_RESOLV(_device_get_parent_with_subsystem_devtype), - UDEV_RESOLV(_device_get_sysattr_value), - UDEV_RESOLV(_device_new_from_syspath), - UDEV_RESOLV(_device_unref), - UDEV_RESOLV(_enumerate_add_match_property), - UDEV_RESOLV(_enumerate_add_match_subsystem), - UDEV_RESOLV(_enumerate_get_list_entry), - UDEV_RESOLV(_enumerate_new), - UDEV_RESOLV(_enumerate_scan_devices), - UDEV_RESOLV(_enumerate_unref), - UDEV_RESOLV(_list_entry_get_name), - UDEV_RESOLV(_list_entry_get_next), - UDEV_RESOLV(_new), - UDEV_RESOLV(_unref) + UDEV_RESOLV(_device_get_devnode), + UDEV_RESOLV(_device_get_parent_with_subsystem_devtype), + UDEV_RESOLV(_device_get_sysattr_value), + UDEV_RESOLV(_device_new_from_syspath), + UDEV_RESOLV(_device_unref), + UDEV_RESOLV(_enumerate_add_match_property), + UDEV_RESOLV(_enumerate_add_match_subsystem), + UDEV_RESOLV(_enumerate_get_list_entry), + UDEV_RESOLV(_enumerate_new), + UDEV_RESOLV(_enumerate_scan_devices), + UDEV_RESOLV(_enumerate_unref), + UDEV_RESOLV(_list_entry_get_name), + UDEV_RESOLV(_list_entry_get_next), + UDEV_RESOLV(_new), + UDEV_RESOLV(_unref) }; static struct FuncToResolv drm2Table[] = { - LIBDRM_RESOLV(Open), - LIBDRM_RESOLV(Close), - LIBDRM_RESOLV(GetVersion), - LIBDRM_RESOLV(FreeVersion), - LIBDRM_RESOLV(DropMaster), - LIBDRM_RESOLV(CommandWrite) + LIBDRM_RESOLV(Open), + LIBDRM_RESOLV(Close), + LIBDRM_RESOLV(GetVersion), + LIBDRM_RESOLV(FreeVersion), + LIBDRM_RESOLV(DropMaster), + LIBDRM_RESOLV(CommandWrite) }; struct Udev1Interface *udevi = NULL; @@ -95,20 +95,20 @@ static void *dlhandle; void resolutionDLClose(void) { - if (udevi) { - free(udevi); - udevi = NULL; - } - - if (drmi) { - free(drmi); - drmi = NULL; - } - - if (dlhandle) { - dlclose(dlhandle); - dlhandle = NULL; - } + if (udevi) { + free(udevi); + udevi = NULL; + } + + if (drmi) { + free(drmi); + drmi = NULL; + } + + if (dlhandle) { + dlclose(dlhandle); + dlhandle = NULL; + } } /* @@ -128,47 +128,47 @@ resolutionDLClose(void) */ static int resolutionDLResolve(void **ptr, // OUT: pointer to - // function table. - size_t size, // IN: Size of ft. - const char name[], // IN: Library name. - const struct FuncToResolv table[], // IN: Table of name- - // offset pairs - int numEntries) // IN: Num entries in + // function table. + size_t size, // IN: Size of ft. + const char name[], // IN: Library name. + const struct FuncToResolv table[], // IN: Table of name- + // offset pairs + int numEntries) // IN: Num entries in // table. { - void **func_ptr; - int i; - - if (*ptr) - return 0; - - *ptr = malloc(size); - if (!*ptr) - return -1; - - dlhandle = dlopen(name, RTLD_NOW); - if (!dlhandle) { - g_debug("%s: Failed to open shared library \"%s\".\n", __func__, - name); - goto out_err; - } - - for (i = 0; i < numEntries; ++i) { - func_ptr = (void *) ((unsigned long) *ptr + table[i].offset); - *func_ptr = dlsym(dlhandle, table[i].name); - if (!*func_ptr) { - g_debug("%s: Failed to resolve %s symbol \"%s\".\n", __func__, - name,table[i].name); - goto out_err; - } - } - - return 0; - - out_err: - resolutionDLClose(); - - return -1; + void **func_ptr; + int i; + + if (*ptr) + return 0; + + *ptr = malloc(size); + if (!*ptr) + return -1; + + dlhandle = dlopen(name, RTLD_NOW); + if (!dlhandle) { + g_debug("%s: Failed to open shared library \"%s\".\n", __func__, + name); + goto out_err; + } + + for (i = 0; i < numEntries; ++i) { + func_ptr = (void *) ((unsigned long) *ptr + table[i].offset); + *func_ptr = dlsym(dlhandle, table[i].name); + if (!*func_ptr) { + g_debug("%s: Failed to resolve %s symbol \"%s\".\n", __func__, + name,table[i].name); + goto out_err; + } + } + + return 0; + +out_err: + resolutionDLClose(); + + return -1; } /* @@ -190,18 +190,18 @@ resolutionDLResolve(void **ptr, // OUT: pointer to int resolutionDLOpen(void) { - /* We support libudev major versions 0 and 1 for now. */ - if (resolutionDLResolve((void **)&udevi, sizeof(*udevi), "libudev.so.1", - udev1Table, ARRAYSIZE(udev1Table)) && - resolutionDLResolve((void **)&udevi, sizeof(*udevi), "libudev.so.0", - udev1Table, ARRAYSIZE(udev1Table))) - return -1; - - if (resolutionDLResolve((void **)&drmi, sizeof(*drmi), "libdrm.so.2", - drm2Table, ARRAYSIZE(drm2Table))) - return -1; - - return 0; + /* We support libudev major versions 0 and 1 for now. */ + if (resolutionDLResolve((void **)&udevi, sizeof(*udevi), "libudev.so.1", + udev1Table, ARRAYSIZE(udev1Table)) && + resolutionDLResolve((void **)&udevi, sizeof(*udevi), "libudev.so.0", + udev1Table, ARRAYSIZE(udev1Table))) + return -1; + + if (resolutionDLResolve((void **)&drmi, sizeof(*drmi), "libdrm.so.2", + drm2Table, ARRAYSIZE(drm2Table))) + return -1; + + return 0; } #endif /* !HAVE_LIBUDEV */ diff --git a/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c b/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c index 46b5b3dd6..52b721484 100644 --- a/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c +++ b/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2008-2017,2019-2020 VMware, Inc. All rights reserved. + * Copyright (C) 2008-2021 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -81,6 +81,83 @@ static Bool SelectResolution(uint32 width, uint32 height); static int ResolutionX11ErrorHandler(Display *d, XErrorEvent *e); +/* + *----------------------------------------------------------------------------- + * + * resolutionXorgDriverVersion -- + * + * Scans for VMWare Xorg driver files and tries to determine the Xorg + * driver version. + * + * Results: + * If succesful returns zero and outputs the driver version in the + * parameters major, minor and level. If not successful, returns -1. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ +static int +resolutionXorgDriverVersion(int numPaths, // IN: Number of strings + // in paths. + const char *paths[], // IN: Possible driver + // paths. + const char versionString[], // IN: Version token. + int *major, // OUT: Major version # + int *minor, // OUT: Minor version # + int *level) // OUT: Patchlevel + // version # +{ + FILE *driver = NULL; + const char *curMatch; + int curFileChar; + int i; + + g_debug("%s: Scanning for VMWare Xorg drivers.\n", __func__); + for(i = 0; i < numPaths; ++i) { + g_debug("%s: Looking for \"%s\".\n", __func__, paths[i]); + driver = fopen(paths[i], "r"); + if (driver) + break; + } + + if (!driver) { + g_debug("%s: No driver found.\n", __func__); + return -1; + } + + g_debug("%s: Driver found. Looking for version info.\n", __func__); + curMatch = versionString; + while (*curMatch) { + if (feof(driver)) + goto outNotFound; + + curFileChar = fgetc(driver); + if (curFileChar != EOF && curFileChar == *curMatch) { + curMatch++; + continue; + } else if (curMatch != versionString) { + curMatch = versionString; + (void) ungetc(curFileChar, driver); + } + } + + if (fscanf(driver, "%d.%d.%d", major, minor, level) != 3) + goto outNotFound; + + fclose(driver); + g_debug("%s: Version info found: %d.%d.%d\n", __func__, *major, *minor, + *level); + return 0; + +outNotFound: + fclose(driver); + g_debug("%s: No version info found.\n", __func__); + return -1; +} + + /* * Global function definitions */