diff --git a/greeter/CMakeLists.txt b/greeter/CMakeLists.txt --- a/greeter/CMakeLists.txt +++ b/greeter/CMakeLists.txt @@ -13,6 +13,7 @@ main.cpp noaccessnetworkaccessmanagerfactory.cpp wallpaper_integration.cpp + kwinglplatform.cpp ) if(HAVE_SECCOMP) diff --git a/greeter/autotests/CMakeLists.txt b/greeter/autotests/CMakeLists.txt --- a/greeter/autotests/CMakeLists.txt +++ b/greeter/autotests/CMakeLists.txt @@ -32,7 +32,7 @@ # Seccomp Test ####################################### if(HAVE_SECCOMP) - add_executable(seccompTest seccomp_test.cpp ../seccomp_filter.cpp) + add_executable(seccompTest seccomp_test.cpp ../seccomp_filter.cpp ../kwinglplatform.cpp) add_test(kscreenlocker-seccompTest seccompTest) ecm_mark_as_test(seccompTest) target_link_libraries(seccompTest diff --git a/greeter/autotests/seccomp_test.cpp b/greeter/autotests/seccomp_test.cpp --- a/greeter/autotests/seccomp_test.cpp +++ b/greeter/autotests/seccomp_test.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include #include "../seccomp_filter.h" +#include "../kwinglplatform.h" #include #include @@ -55,6 +56,9 @@ void SeccompTest::testOpenFile() { + if (KWin::GLPlatform::instance()->driver() == KWin::Driver_NVidia) { + QSKIP("Write protection not supported on NVIDIA"); + } QFile file(QStringLiteral(KCHECKPASS_BIN)); QVERIFY(file.exists()); QVERIFY(!file.open(QIODevice::WriteOnly)); diff --git a/greeter/kwinglplatform.h b/greeter/kwinglplatform.h new file mode 100644 --- /dev/null +++ b/greeter/kwinglplatform.h @@ -0,0 +1,416 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2010 Fredrik Höglund + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#ifndef KWIN_GLPLATFORM_H +#define KWIN_GLPLATFORM_H + +#include +#include + +namespace KWin +{ +// forward declare method +void cleanupGL(); + +inline qint64 kVersionNumber(qint64 major, qint64 minor, qint64 patch = 0) +{ + return ((major & 0xffff) << 32) | ((minor & 0xffff) << 16) | (patch & 0xffff); +} + +enum GLFeature { + /** + * Set when a texture bound to a pixmap uses the same storage as the pixmap, + * and thus doesn't need to be rebound when the contents of the pixmap + * has changed. + */ + LooseBinding, + + /** + * Set if the driver supports the following extensions: + * - GL_ARB_shader_objects + * - GL_ARB_fragment_shader + * - GL_ARB_vertex_shader + * - GL_ARB_shading_language_100 + */ + GLSL, + + /** + * If set, assume the following: + * - No flow control or branches + * - No loops, unless the loops have a fixed iteration count and can be unrolled + * - No functions, unless they can be inlined + * - No indirect indexing of arrays + * - No support for gl_ClipVertex or gl_FrontFacing + * - No texture fetches in vertex shaders + * - Max 32 texture fetches in fragment shaders + * - Max 4 texture indirections + */ + LimitedGLSL, + + /** + * Set when the driver supports GL_ARB_texture_non_power_of_two. + */ + TextureNPOT, + + /** + * If set, the driver supports GL_ARB_texture_non_power_of_two with the + * GL_ARB_texture_rectangle limitations. + * + * This means no support for mipmap filters, and that only the following + * wrap modes are supported: + * - GL_CLAMP + * - GL_CLAMP_TO_EDGE + * - GL_CLAMP_TO_BORDER + */ + LimitedNPOT +}; + +enum Driver { + Driver_R100, // Technically "Radeon" + Driver_R200, + Driver_R300C, + Driver_R300G, + Driver_R600C, + Driver_R600G, + Driver_Nouveau, + Driver_Intel, + Driver_NVidia, + Driver_Catalyst, + Driver_Swrast, + Driver_Softpipe, + Driver_Llvmpipe, + Driver_VirtualBox, + Driver_VMware, + Driver_Qualcomm, + Driver_Unknown +}; + +enum ChipClass { + // Radeon + R100 = 0, // GL1.3 DX7 2000 + R200, // GL1.4 DX8.1 SM 1.4 2001 + R300, // GL2.0 DX9 SM 2.0 2002 + R400, // GL2.0 DX9b SM 2.0b 2004 + R500, // GL2.0 DX9c SM 3.0 2005 + R600, // GL3.3 DX10 SM 4.0 2006 + R700, // GL3.3 DX10.1 SM 4.1 2008 + Evergreen, // GL4.0 CL1.0 DX11 SM 5.0 2009 + NorthernIslands, // GL4.0 CL1.1 DX11 SM 5.0 2010 + UnknownRadeon = 999, + + // NVIDIA + NV10 = 1000, // GL1.2 DX7 1999 + NV20, // GL1.3 DX8 SM 1.1 2001 + NV30, // GL1.5 DX9a SM 2.0 2003 + NV40, // GL2.1 DX9c SM 3.0 2004 + G80, // GL3.3 DX10 SM 4.0 2006 + GF100, // GL4.1 CL1.1 DX11 SM 5.0 2010 + UnknownNVidia = 1999, + + // Intel + I8XX = 2000, // GL1.3 DX7 2001 + I915, // GL1.4/1.5 DX9/DX9c SM 2.0 2004 + I965, // GL2.0/2.1 DX9/DX10 SM 3.0/4.0 2006 + SandyBridge, // GL3.1 CL1.1 DX10.1 SM 4.0 2010 + IvyBridge, // GL4.0 CL1.1 DX11 SM 5.0 2012 + Haswell, // GL4.0 CL1.2 DX11.1 SM 5.0 2013 + UnknownIntel = 2999, + + // Qualcomm Adreno + // from https://en.wikipedia.org/wiki/Adreno + Adreno1XX = 3000, // GLES1.1 + Adreno2XX, // GLES2.0 DX9c + Adreno3XX, // GLES3.0 CL1.1 DX11.1 + Adreno4XX, // GLES3.1 CL1.2 DX11.2 + Adreno5XX, // GLES3.1 CL2.0 DX11.2 + UnknownAdreno = 3999, + + UnknownChipClass = 99999 +}; + + +class GLPlatform +{ +public: + ~GLPlatform(); + + /** + * Runs the detection code using the current OpenGL context. + */ + void detect(); + + /** + * Prints the results of the detection code. + */ + void printResults() const; + + /** + * Returns a pointer to the GLPlatform instance. + */ + static GLPlatform *instance(); + + /** + * Returns true if the driver support the given feature, and false otherwise. + */ + bool supports(GLFeature feature) const; + + /** + * Returns the OpenGL version. + */ + qint64 glVersion() const; + + /** + * Returns the GLSL version if the driver supports GLSL, and 0 otherwise. + */ + qint64 glslVersion() const; + + /** + * Returns the Mesa version if the driver is a Mesa driver, and 0 otherwise. + */ + qint64 mesaVersion() const; + + /** + * Returns the Gallium version if the driver is a Gallium driver, and 0 otherwise. + */ + qint64 galliumVersion() const; + + /** + * Returns the X server version. + * + * Note that the version number changed from 7.2 to 1.3 in the first release + * following the doupling of the X server from the katamari. + * + * For non X.org servers, this method returns 0. + */ + qint64 serverVersion() const; + + /** + * Returns the Linux kernel version. + * + * If the kernel is not a Linux kernel, this method returns 0. + */ + qint64 kernelVersion() const; + + /** + * Returns the driver version. + * + * For Mesa drivers, this is the same as the Mesa version number. + */ + qint64 driverVersion() const; + + /** + * Returns the driver. + */ + Driver driver() const; + + /** + * Returns the chip class. + */ + ChipClass chipClass() const; + + /** + * Returns true if the driver is a Mesa driver, and false otherwise. + */ + bool isMesaDriver() const; + + /** + * Returns true if the driver is a Gallium driver, and false otherwise. + */ + bool isGalliumDriver() const; + + /** + * Returns true if the GPU is a Radeon GPU, and false otherwise. + */ + bool isRadeon() const; + + /** + * Returns true if the GPU is an NVIDIA GPU, and false otherwise. + */ + bool isNvidia() const; + + /** + * Returns true if the GPU is an Intel GPU, and false otherwise. + */ + bool isIntel() const; + + /** + * @returns @c true if the "GPU" is a VirtualBox GPU, and @c false otherwise. + * @since 4.10 + **/ + bool isVirtualBox() const; + + /** + * @returns @c true if the "GPU" is a VMWare GPU, and @c false otherwise. + * @since 4.10 + **/ + bool isVMware() const; + + /** + * @returns @c true if OpenGL is emulated in software. + * @since 4.7 + **/ + bool isSoftwareEmulation() const; + + /** + * @returns @c true if the driver is known to be from a virtual machine. + * @since 4.10 + **/ + bool isVirtualMachine() const; + + /** + * @returns @c true if the GPU is a Qualcomm Adreno GPU, and false otherwise + * @since 5.8 + **/ + bool isAdreno() const; + + /** + * @returns the GL_VERSION string as provided by the driver. + * @since 4.9 + **/ + const QByteArray &glVersionString() const; + /** + * @returns the GL_RENDERER string as provided by the driver. + * @since 4.9 + **/ + const QByteArray &glRendererString() const; + /** + * @returns the GL_VENDOR string as provided by the driver. + * @since 4.9 + **/ + const QByteArray &glVendorString() const; + /** + * @returns the GL_SHADING_LANGUAGE_VERSION string as provided by the driver. + * If the driver does not support the OpenGL Shading Language a null bytearray is returned. + * @since 4.9 + **/ + const QByteArray &glShadingLanguageVersionString() const; + /** + * @returns Whether the driver supports loose texture binding. + * @since 4.9 + **/ + bool isLooseBinding() const; + /** + * @returns Whether OpenGL ES is used + */ + bool isGLES() const; + + /** + * Returns true if glMapBufferRange() is likely to perform worse than glBufferSubData() + * when updating an unused range of a buffer object, and false otherwise. + * + * @since 4.11 + */ + bool preferBufferSubData() const; + + /** + * @returns a human readable form of the @p version as a QString. + * @since 4.9 + * @see glVersion + * @see glslVersion + * @see driverVersion + * @see mesaVersion + * @see galliumVersion + * @see kernelVersion + * @see serverVersion + **/ + static QString versionToString(qint64 version); + /** + * @returns a human readable form of the @p version as a QByteArray. + * @since 5.5 + * @see glVersion + * @see glslVersion + * @see driverVersion + * @see mesaVersion + * @see galliumVersion + * @see kernelVersion + * @see serverVersion + **/ + static QByteArray versionToString8(qint64 version); + + /** + * @returns a human readable form for the @p driver as a QString. + * @since 4.9 + * @see driver + **/ + static QString driverToString(Driver driver); + /** + * @returns a human readable form for the @p driver as a QByteArray. + * @since 5.5 + * @see driver + **/ + static QByteArray driverToString8(Driver driver); + + /** + * @returns a human readable form for the @p chipClass as a QString. + * @since 4.9 + * @see chipClass + **/ + static QString chipClassToString(ChipClass chipClass); + /** + * @returns a human readable form for the @p chipClass as a QByteArray. + * @since 5.5 + * @see chipClass + **/ + static QByteArray chipClassToString8(ChipClass chipClass); + +private: + GLPlatform(); + friend void KWin::cleanupGL(); + static void cleanup(); + +private: + QByteArray m_renderer; + QByteArray m_vendor; + QByteArray m_version; + QByteArray m_glsl_version; + QByteArray m_chipset; + QSet m_extensions; + Driver m_driver; + ChipClass m_chipClass; + qint64 m_glVersion; + qint64 m_glslVersion; + qint64 m_mesaVersion; + qint64 m_driverVersion; + qint64 m_galliumVersion; + qint64 m_serverVersion; + qint64 m_kernelVersion; + bool m_looseBinding: 1; + bool m_supportsGLSL: 1; + bool m_limitedGLSL: 1; + bool m_textureNPOT: 1; + bool m_limitedNPOT: 1; + bool m_virtualMachine: 1; + bool m_preferBufferSubData: 1; + bool m_gles: 1; + static GLPlatform *s_platform; +}; + +inline GLPlatform *GLPlatform::instance() +{ + if (!s_platform) + s_platform = new GLPlatform; + + return s_platform; +} + +} // namespace KWin + +#endif // KWIN_GLPLATFORM_H + diff --git a/greeter/kwinglplatform.cpp b/greeter/kwinglplatform.cpp new file mode 100644 --- /dev/null +++ b/greeter/kwinglplatform.cpp @@ -0,0 +1,1062 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2010 Fredrik Höglund + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "kwinglplatform.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace KWin +{ + +GLPlatform *GLPlatform::s_platform = 0; + +static qint64 parseVersionString(const QByteArray &version) +{ + // Skip any leading non digit + int start = 0; + while (start < version.length() && !QChar::fromLatin1(version[start]).isDigit()) + start++; + + // Strip any non digit, non '.' characters from the end + int end = start; + while (end < version.length() && (version[end] == '.' || QChar::fromLatin1(version[end]).isDigit())) + end++; + + const QByteArray result = version.mid(start, end-start); + const QList tokens = result.split('.'); + const qint64 major = tokens.at(0).toInt(); + const qint64 minor = tokens.count() > 1 ? tokens.at(1).toInt() : 0; + const qint64 patch = tokens.count() > 2 ? tokens.at(2).toInt() : 0; + + return kVersionNumber(major, minor, patch); +} + +static qint64 getKernelVersion() +{ + struct utsname name; + uname(&name); + + if (qstrcmp(name.sysname, "Linux") == 0) + return parseVersionString(name.release); + + return 0; +} + +// Extracts the portion of a string that matches a regular expression +static QString extract(const QString &string, const QString &match, int offset = 0) +{ + QString result; + QRegExp rx(match); + int pos = rx.indexIn(string, offset); + if (pos != -1) + result = string.mid(pos, rx.matchedLength()); + return result; +} + +static ChipClass detectRadeonClass(const QByteArray &chipset) +{ + if (chipset.isEmpty()) + return UnknownRadeon; + + if (chipset.contains("R100") || + chipset.contains("RV100") || + chipset.contains("RS100")) + return R100; + + if (chipset.contains("RV200") || + chipset.contains("RS200") || + chipset.contains("R200") || + chipset.contains("RV250") || + chipset.contains("RS300") || + chipset.contains("RV280")) + return R200; + + if (chipset.contains("R300") || + chipset.contains("R350") || + chipset.contains("R360") || + chipset.contains("RV350") || + chipset.contains("RV370") || + chipset.contains("RV380")) + return R300; + + if (chipset.contains("R420") || + chipset.contains("R423") || + chipset.contains("R430") || + chipset.contains("R480") || + chipset.contains("R481") || + chipset.contains("RV410") || + chipset.contains("RS400") || + chipset.contains("RC410") || + chipset.contains("RS480") || + chipset.contains("RS482") || + chipset.contains("RS600") || + chipset.contains("RS690") || + chipset.contains("RS740")) + return R400; + + if (chipset.contains("RV515") || + chipset.contains("R520") || + chipset.contains("RV530") || + chipset.contains("R580") || + chipset.contains("RV560") || + chipset.contains("RV570")) + return R500; + + if (chipset.contains("R600") || + chipset.contains("RV610") || + chipset.contains("RV630") || + chipset.contains("RV670") || + chipset.contains("RV620") || + chipset.contains("RV635") || + chipset.contains("RS780") || + chipset.contains("RS880")) + return R600; + + if (chipset.contains("R700") || + chipset.contains("RV770") || + chipset.contains("RV730") || + chipset.contains("RV710") || + chipset.contains("RV740")) + return R700; + + if (chipset.contains("EVERGREEN") || // Not an actual chipset, but returned by R600G in 7.9 + chipset.contains("CEDAR") || + chipset.contains("REDWOOD") || + chipset.contains("JUNIPER") || + chipset.contains("CYPRESS") || + chipset.contains("HEMLOCK") || + chipset.contains("PALM")) + return Evergreen; + + if (chipset.contains("SUMO") || + chipset.contains("SUMO2") || + chipset.contains("BARTS") || + chipset.contains("TURKS") || + chipset.contains("CAICOS") || + chipset.contains("CAYMAN")) + return NorthernIslands; + + const QString chipset16 = QString::fromLatin1(chipset); + QString name = extract(chipset16, QStringLiteral("HD [0-9]{4}")); // HD followed by a space and 4 digits + if (!name.isEmpty()) { + const int id = name.rightRef(4).toInt(); + if (id == 6250 || id == 6310) // Palm + return Evergreen; + + if (id >= 6000 && id < 7000) + return NorthernIslands; // HD 6xxx + + if (id >= 5000 && id < 6000) + return Evergreen; // HD 5xxx + + if (id >= 4000 && id < 5000) + return R700; // HD 4xxx + + if (id >= 2000 && id < 4000) // HD 2xxx/3xxx + return R600; + + return UnknownRadeon; + } + + name = extract(chipset16, QStringLiteral("X[0-9]{3,4}")); // X followed by 3-4 digits + if (!name.isEmpty()) { + const int id = name.midRef(1, -1).toInt(); + + // X1xxx + if (id >= 1300) + return R500; + + // X7xx, X8xx, X12xx, 2100 + if ((id >= 700 && id < 1000) || id >= 1200) + return R400; + + // X200, X3xx, X5xx, X6xx, X10xx, X11xx + if ((id >= 300 && id < 700) || (id >= 1000 && id < 1200)) + return R300; + + return UnknownRadeon; + } + + name = extract(chipset16, QStringLiteral("\\b[0-9]{4}\\b")); // A group of 4 digits + if (!name.isEmpty()) { + const int id = name.toInt(); + + // 7xxx + if (id >= 7000 && id < 8000) + return R100; + + // 8xxx, 9xxx + if (id >= 8000 && id < 9500) + return R200; + + // 9xxx + if (id >= 9500) + return R300; + + if (id == 2100) + return R400; + } + + return UnknownRadeon; +} + +static ChipClass detectNVidiaClass(const QString &chipset) +{ + QString name = extract(chipset, QStringLiteral("\\bNV[0-9,A-F]{2}\\b")); // NV followed by two hexadecimal digits + if (!name.isEmpty()) { + const int id = chipset.midRef(2, -1).toInt(0, 16); // Strip the 'NV' from the id + + switch(id & 0xf0) { + case 0x00: + case 0x10: + return NV10; + + case 0x20: + return NV20; + + case 0x30: + return NV30; + + case 0x40: + case 0x60: + return NV40; + + case 0x50: + case 0x80: + case 0x90: + case 0xA0: + return G80; + + default: + return UnknownNVidia; + } + } + + if (chipset.contains(QLatin1String("GeForce2")) || chipset.contains(QLatin1String("GeForce 256"))) + return NV10; + + if (chipset.contains(QLatin1String("GeForce3"))) + return NV20; + + if (chipset.contains(QLatin1String("GeForce4"))) { + if (chipset.contains(QLatin1String("MX 420")) || + chipset.contains(QLatin1String("MX 440")) || // including MX 440SE + chipset.contains(QLatin1String("MX 460")) || + chipset.contains(QLatin1String("MX 4000")) || + chipset.contains(QLatin1String("PCX 4300"))) + return NV10; + + return NV20; + } + + // GeForce 5,6,7,8,9 + name = extract(chipset, QStringLiteral("GeForce (FX |PCX |Go )?\\d{4}(M|\\b)")).trimmed(); + if (!name.isEmpty()) { + if (!name[name.length() - 1].isDigit()) + name.chop(1); + + const int id = name.rightRef(4).toInt(); + if (id < 6000) + return NV30; + + if (id >= 6000 && id < 8000) + return NV40; + + if (id >= 8000) + return G80; + + return UnknownNVidia; + } + + // GeForce 100/200/300/400/500 + name = extract(chipset, QStringLiteral("GeForce (G |GT |GTX |GTS )?\\d{3}(M|\\b)")).trimmed(); + if (!name.isEmpty()) { + if (!name[name.length() - 1].isDigit()) + name.chop(1); + + const int id = name.rightRef(3).toInt(); + if (id >= 100 && id < 600) { + if (id >= 400) + return GF100; + + return G80; + } + return UnknownNVidia; + } + + return UnknownNVidia; +} +static inline ChipClass detectNVidiaClass(const QByteArray &chipset) +{ + return detectNVidiaClass(QString::fromLatin1(chipset)); +} + +static ChipClass detectIntelClass(const QByteArray &chipset) +{ + // see mesa repository: src/mesa/drivers/dri/intel/intel_context.c + // GL 1.3, DX8? SM ? + if (chipset.contains("845G") || + chipset.contains("830M") || + chipset.contains("852GM/855GM") || + chipset.contains("865G")) + return I8XX; + + // GL 1.4, DX 9.0, SM 2.0 + if (chipset.contains("915G") || + chipset.contains("E7221G") || + chipset.contains("915GM") || + chipset.contains("945G") || // DX 9.0c + chipset.contains("945GM") || + chipset.contains("945GME") || + chipset.contains("Q33") || // GL1.5 + chipset.contains("Q35") || + chipset.contains("G33") || + chipset.contains("965Q") || // GMA 3000, but apparently considered gen 4 by the driver + chipset.contains("946GZ") || // GMA 3000, but apparently considered gen 4 by the driver + chipset.contains("IGD")) + return I915; + + // GL 2.0, DX 9.0c, SM 3.0 + if (chipset.contains("965G") || + chipset.contains("G45/G43") || // SM 4.0 + chipset.contains("965GM") || // GL 2.1 + chipset.contains("965GME/GLE") || + chipset.contains("GM45") || + chipset.contains("Q45/Q43") || + chipset.contains("G41") || + chipset.contains("B43") || + chipset.contains("Ironlake")) + return I965; + + // GL 3.1, CL 1.1, DX 10.1 + if (chipset.contains("Sandybridge")) { + return SandyBridge; + } + + // GL4.0, CL1.1, DX11, SM 5.0 + if (chipset.contains("Ivybridge")) { + return IvyBridge; + } + + // GL4.0, CL1.2, DX11.1, SM 5.0 + if (chipset.contains("Haswell")) { + return Haswell; + } + + return UnknownIntel; +} + +static ChipClass detectQualcommClass(const QByteArray &chipClass) +{ + if (!chipClass.contains("Adreno")) { + return UnknownChipClass; + } + const auto parts = chipClass.split(' '); + if (parts.count() < 3) { + return UnknownAdreno; + } + bool ok = false; + const int value = parts.at(2).toInt(&ok); + if (ok) { + if (value >= 100 && value < 200) { + return Adreno1XX; + } + if (value >= 200 && value < 300) { + return Adreno2XX; + } + if (value >= 300 && value < 400) { + return Adreno3XX; + } + if (value >= 400 && value < 500) { + return Adreno4XX; + } + if (value >= 500 && value < 600) { + return Adreno5XX; + } + } + return UnknownAdreno; +} + +QString GLPlatform::versionToString(qint64 version) +{ + return QString::fromLatin1(versionToString8(version)); +} +QByteArray GLPlatform::versionToString8(qint64 version) +{ + int major = (version >> 32); + int minor = (version >> 16) & 0xffff; + int patch = version & 0xffff; + + QByteArray string = QByteArray::number(major) + '.' + QByteArray::number(minor); + if (patch != 0) + string += '.' + QByteArray::number(patch); + + return string; +} + +QString GLPlatform::driverToString(Driver driver) +{ + return QString::fromLatin1(driverToString8(driver)); +} +QByteArray GLPlatform::driverToString8(Driver driver) +{ + switch(driver) { + case Driver_R100: + return QByteArrayLiteral("Radeon"); + case Driver_R200: + return QByteArrayLiteral("R200"); + case Driver_R300C: + return QByteArrayLiteral("R300C"); + case Driver_R300G: + return QByteArrayLiteral("R300G"); + case Driver_R600C: + return QByteArrayLiteral("R600C"); + case Driver_R600G: + return QByteArrayLiteral("R600G"); + case Driver_Nouveau: + return QByteArrayLiteral("Nouveau"); + case Driver_Intel: + return QByteArrayLiteral("Intel"); + case Driver_NVidia: + return QByteArrayLiteral("NVIDIA"); + case Driver_Catalyst: + return QByteArrayLiteral("Catalyst"); + case Driver_Swrast: + return QByteArrayLiteral("Software rasterizer"); + case Driver_Softpipe: + return QByteArrayLiteral("softpipe"); + case Driver_Llvmpipe: + return QByteArrayLiteral("LLVMpipe"); + case Driver_VirtualBox: + return QByteArrayLiteral("VirtualBox (Chromium)"); + case Driver_VMware: + return QByteArrayLiteral("VMware (SVGA3D)"); + case Driver_Qualcomm: + return QByteArrayLiteral("Qualcomm"); + + default: + return QByteArrayLiteral("Unknown"); + } +} + +QString GLPlatform::chipClassToString(ChipClass chipClass) +{ + return QString::fromLatin1(chipClassToString8(chipClass)); +} +QByteArray GLPlatform::chipClassToString8(ChipClass chipClass) +{ + switch(chipClass) { + case R100: + return QByteArrayLiteral("R100"); + case R200: + return QByteArrayLiteral("R200"); + case R300: + return QByteArrayLiteral("R300"); + case R400: + return QByteArrayLiteral("R400"); + case R500: + return QByteArrayLiteral("R500"); + case R600: + return QByteArrayLiteral("R600"); + case R700: + return QByteArrayLiteral("R700"); + case Evergreen: + return QByteArrayLiteral("EVERGREEN"); + case NorthernIslands: + return QByteArrayLiteral("NI"); + + case NV10: + return QByteArrayLiteral("NV10"); + case NV20: + return QByteArrayLiteral("NV20"); + case NV30: + return QByteArrayLiteral("NV30"); + case NV40: + return QByteArrayLiteral("NV40/G70"); + case G80: + return QByteArrayLiteral("G80/G90"); + case GF100: + return QByteArrayLiteral("GF100"); + + case I8XX: + return QByteArrayLiteral("i830/i835"); + case I915: + return QByteArrayLiteral("i915/i945"); + case I965: + return QByteArrayLiteral("i965"); + case SandyBridge: + return QByteArrayLiteral("SandyBridge"); + case IvyBridge: + return QByteArrayLiteral("IvyBridge"); + case Haswell: + return QByteArrayLiteral("Haswell"); + + case Adreno1XX: + return QByteArrayLiteral("Adreno 1xx series"); + case Adreno2XX: + return QByteArrayLiteral("Adreno 2xx series"); + case Adreno3XX: + return QByteArrayLiteral("Adreno 3xx series"); + case Adreno4XX: + return QByteArrayLiteral("Adreno 4xx series"); + case Adreno5XX: + return QByteArrayLiteral("Adreno 5xx series"); + + default: + return QByteArrayLiteral("Unknown"); + } +} + + + +// ------- + + + +GLPlatform::GLPlatform() + : m_driver(Driver_Unknown), + m_chipClass(UnknownChipClass), + m_glVersion(0), + m_glslVersion(0), + m_mesaVersion(0), + m_driverVersion(0), + m_galliumVersion(0), + m_serverVersion(0), + m_kernelVersion(0), + m_looseBinding(false), + m_supportsGLSL(false), + m_limitedGLSL(false), + m_textureNPOT(false), + m_limitedNPOT(false), + m_virtualMachine(false), + m_preferBufferSubData(false), + m_gles(false) +{ +} + +GLPlatform::~GLPlatform() +{ +} + +void GLPlatform::detect() +{ + QOpenGLFunctions gl; + gl.initializeOpenGLFunctions(); + + m_vendor = (const char*)gl.glGetString(GL_VENDOR); + m_renderer = (const char*)gl.glGetString(GL_RENDERER); + m_version = (const char*)gl.glGetString(GL_VERSION); + + // Parse the OpenGL version + const QList versionTokens = m_version.split(' '); + if (versionTokens.count() > 0) { + const QByteArray version = QByteArray(m_version); + m_glVersion = parseVersionString(version); + if (version.startsWith("OpenGL ES")) { + // from GLES 2: "Returns a version or release number of the form OpenGLES." + // from GLES 3: "Returns a version or release number." and "The version number uses one of these forms: major_number.minor_number major_number.minor_number.release_number" + m_gles = true; + } + } + + const QByteArray extensions = (const char *) gl.glGetString(GL_EXTENSIONS); + m_extensions = QSet::fromList(extensions.split(' ')); + + // Parse the Mesa version + const int mesaIndex = versionTokens.indexOf("Mesa"); + if (mesaIndex != -1) { + const QByteArray version = versionTokens.at(mesaIndex + 1); + m_mesaVersion = parseVersionString(version); + } + + if (isGLES()) { + m_supportsGLSL = true; + m_textureNPOT = true; + } else { + m_supportsGLSL = m_extensions.contains("GL_ARB_shader_objects") && + m_extensions.contains("GL_ARB_fragment_shader") && + m_extensions.contains("GL_ARB_vertex_shader"); + + m_textureNPOT = m_extensions.contains("GL_ARB_texture_non_power_of_two"); + } + + m_kernelVersion = getKernelVersion(); + + m_glslVersion = 0; + m_glsl_version.clear(); + + if (m_supportsGLSL) { + // Parse the GLSL version + m_glsl_version = (const char*)gl.glGetString(GL_SHADING_LANGUAGE_VERSION); + m_glslVersion = parseVersionString(m_glsl_version); + } + + m_chipset = QByteArrayLiteral("Unknown"); + m_preferBufferSubData = false; + + + // Mesa classic drivers + // ==================================================== + + // Radeon + if (m_renderer.startsWith("Mesa DRI R")) { + // Sample renderer string: Mesa DRI R600 (RV740 94B3) 20090101 x86/MMX/SSE2 TCL DRI2 + const QList tokens = m_renderer.split(' '); + const QByteArray chipClass = tokens.at(2); + m_chipset = tokens.at(3).mid(1, -1); // Strip the leading '(' + + if (chipClass == "R100") + // Vendor: Tungsten Graphics, Inc. + m_driver = Driver_R100; + + else if (chipClass == "R200") + // Vendor: Tungsten Graphics, Inc. + m_driver = Driver_R200; + + else if (chipClass == "R300") + // Vendor: DRI R300 Project + m_driver = Driver_R300C; + + else if (chipClass == "R600") + // Vendor: Advanced Micro Devices, Inc. + m_driver = Driver_R600C; + + m_chipClass = detectRadeonClass(m_chipset); + } + + // Intel + else if (m_renderer.contains("Intel")) { + // Vendor: Tungsten Graphics, Inc. + // Sample renderer string: Mesa DRI Mobile Intel® GM45 Express Chipset GEM 20100328 2010Q1 + + QByteArray chipset; + if (m_renderer.startsWith("Intel(R) Integrated Graphics Device")) + chipset = "IGD"; + else + chipset = m_renderer; + + m_driver = Driver_Intel; + m_chipClass = detectIntelClass(chipset); + } + + // Gallium drivers + // ==================================================== + else if (m_renderer.contains("Gallium")) { + // Sample renderer string: Gallium 0.4 on AMD RV740 + const QList tokens = m_renderer.split(' '); + m_galliumVersion = parseVersionString(tokens.at(1)); + m_chipset = (tokens.at(3) == "AMD" || tokens.at(3) == "ATI") ? + tokens.at(4) : tokens.at(3); + + // R300G + if (m_vendor == QByteArrayLiteral("X.Org R300 Project")) { + m_chipClass = detectRadeonClass(m_chipset); + m_driver = Driver_R300G; + } + + // R600G + else if (m_vendor == "X.Org" && + (m_renderer.contains("R6") || + m_renderer.contains("R7") || + m_renderer.contains("RV6") || + m_renderer.contains("RV7") || + m_renderer.contains("RS780") || + m_renderer.contains("RS880") || + m_renderer.contains("CEDAR") || + m_renderer.contains("REDWOOD") || + m_renderer.contains("JUNIPER") || + m_renderer.contains("CYPRESS") || + m_renderer.contains("HEMLOCK") || + m_renderer.contains("PALM") || + m_renderer.contains("EVERGREEN") || + m_renderer.contains("SUMO") || + m_renderer.contains("SUMO2") || + m_renderer.contains("BARTS") || + m_renderer.contains("TURKS") || + m_renderer.contains("CAICOS") || + m_renderer.contains("CAYMAN"))) { + m_chipClass = detectRadeonClass(m_chipset); + m_driver = Driver_R600G; + } + + // Nouveau + else if (m_vendor == "nouveau") { + m_chipClass = detectNVidiaClass(m_chipset); + m_driver = Driver_Nouveau; + } + + // softpipe + else if (m_vendor == "VMware, Inc." && m_chipset == "softpipe" ) { + m_driver = Driver_Softpipe; + } + + // llvmpipe + else if (m_vendor == "VMware, Inc." && m_chipset == "llvmpipe") { + m_driver = Driver_Llvmpipe; + } + + // SVGA3D + else if (m_vendor == "VMware, Inc." && m_chipset.contains("SVGA3D")) { + m_driver = Driver_VMware; + } + } + + + // Properietary drivers + // ==================================================== + else if (m_vendor == "ATI Technologies Inc.") { + m_chipClass = detectRadeonClass(m_renderer); + m_driver = Driver_Catalyst; + + if (versionTokens.count() > 1 && versionTokens.at(2)[0] == '(') + m_driverVersion = parseVersionString(versionTokens.at(1)); + else if (versionTokens.count() > 0) + m_driverVersion = parseVersionString(versionTokens.at(0)); + else + m_driverVersion = 0; + } + + else if (m_vendor == "NVIDIA Corporation") { + m_chipClass = detectNVidiaClass(m_renderer); + m_driver = Driver_NVidia; + + int index = versionTokens.indexOf("NVIDIA"); + if (versionTokens.count() > index) + m_driverVersion = parseVersionString(versionTokens.at(index + 1)); + else + m_driverVersion = 0; + } + + else if (m_vendor == "Qualcomm") { + m_driver = Driver_Qualcomm; + m_chipClass = detectQualcommClass(m_renderer); + } + + else if (m_renderer == "Software Rasterizer") { + m_driver = Driver_Swrast; + } + + // Virtual Hardware + // ==================================================== + else if (m_vendor == "Humper" && m_renderer == "Chromium") { + // Virtual Box + m_driver = Driver_VirtualBox; + + const int index = versionTokens.indexOf("Chromium"); + if (versionTokens.count() > index) + m_driverVersion = parseVersionString(versionTokens.at(index + 1)); + else + m_driverVersion = 0; + } + + + // Driver/GPU specific features + // ==================================================== + if (isRadeon()) { + // R200 technically has a programmable pipeline, but since it's SM 1.4, + // it's too limited to to be of any practical value to us. + if (m_chipClass < R300) + m_supportsGLSL = false; + + m_limitedGLSL = false; + m_limitedNPOT = false; + + if (m_chipClass < R600) { + if (driver() == Driver_Catalyst) + m_textureNPOT = m_limitedNPOT = false; // Software fallback + else if (driver() == Driver_R300G) + m_limitedNPOT = m_textureNPOT; + + m_limitedGLSL = m_supportsGLSL; + } + + if (driver() == Driver_R600G || + (driver() == Driver_R600C && m_renderer.contains("DRI2"))) { + m_looseBinding = true; + } + } + + if (isNvidia()) { + if (m_driver == Driver_NVidia && m_chipClass < NV40) + m_supportsGLSL = false; // High likelihood of software emulation + + if (m_driver == Driver_NVidia) { + m_looseBinding = true; + m_preferBufferSubData = true; + } + + m_limitedNPOT = m_textureNPOT && m_chipClass < NV40; + m_limitedGLSL = m_supportsGLSL && m_chipClass < G80; + } + + if (isIntel()) { + if (m_chipClass < I915) + m_supportsGLSL = false; + + m_limitedGLSL = m_supportsGLSL && m_chipClass < I965; + // see https://bugs.freedesktop.org/show_bug.cgi?id=80349#c1 + m_looseBinding = false; + } + + if (isSoftwareEmulation()) { + if (m_driver < Driver_Llvmpipe) { + // Software emulation does not provide GLSL + m_limitedGLSL = m_supportsGLSL = false; + } else { + m_limitedGLSL = false; + m_supportsGLSL = true; + } + } + + if (m_chipClass == UnknownChipClass && m_driver == Driver_Unknown) { + // we don't know the hardware. Let's be optimistic and assume OpenGL compatible hardware + m_supportsGLSL = true; + } + + if (isVirtualBox()) { + m_virtualMachine = true; + } + + if (isVMware()) { + m_virtualMachine = true; + } + + // and force back to shader supported on gles, we wouldn't have got a context if not supported + if (isGLES()) { + m_supportsGLSL = true; + m_limitedGLSL = false; + } +} + +static void print(const QByteArray &label, const QByteArray &setting) +{ + std::cout << std::setw(40) << std::left + << label.data() << setting.data() << std::endl; +} + +void GLPlatform::printResults() const +{ + print(QByteArrayLiteral("OpenGL vendor string:"), m_vendor); + print(QByteArrayLiteral("OpenGL renderer string:"), m_renderer); + print(QByteArrayLiteral("OpenGL version string:"), m_version); + + if (m_supportsGLSL) + print(QByteArrayLiteral("OpenGL shading language version string:"), m_glsl_version); + + print(QByteArrayLiteral("Driver:"), driverToString8(m_driver)); + if (!isMesaDriver()) + print(QByteArrayLiteral("Driver version:"), versionToString8(m_driverVersion)); + + print(QByteArrayLiteral("GPU class:"), chipClassToString8(m_chipClass)); + + print(QByteArrayLiteral("OpenGL version:"), versionToString8(m_glVersion)); + + if (m_supportsGLSL) + print(QByteArrayLiteral("GLSL version:"), versionToString8(m_glslVersion)); + + if (isMesaDriver()) + print(QByteArrayLiteral("Mesa version:"), versionToString8(mesaVersion())); + //if (galliumVersion() > 0) + // print("Gallium version:", versionToString(m_galliumVersion)); + if (serverVersion() > 0) + print(QByteArrayLiteral("X server version:"), versionToString8(m_serverVersion)); + if (kernelVersion() > 0) + print(QByteArrayLiteral("Linux kernel version:"), versionToString8(m_kernelVersion)); + + print(QByteArrayLiteral("Requires strict binding:"), !m_looseBinding ? QByteArrayLiteral("yes") : QByteArrayLiteral("no")); + print(QByteArrayLiteral("GLSL shaders:"), m_supportsGLSL ? (m_limitedGLSL ? QByteArrayLiteral("limited") : QByteArrayLiteral("yes")) : QByteArrayLiteral("no")); + print(QByteArrayLiteral("Texture NPOT support:"), m_textureNPOT ? (m_limitedNPOT ? QByteArrayLiteral("limited") : QByteArrayLiteral("yes")) : QByteArrayLiteral("no")); + print(QByteArrayLiteral("Virtual Machine:"), m_virtualMachine ? QByteArrayLiteral("yes") : QByteArrayLiteral("no")); +} + +bool GLPlatform::supports(GLFeature feature) const +{ + switch(feature) { + case LooseBinding: + return m_looseBinding; + + case GLSL: + return m_supportsGLSL; + + case LimitedGLSL: + return m_limitedGLSL; + + case TextureNPOT: + return m_textureNPOT; + + case LimitedNPOT: + return m_limitedNPOT; + + default: + return false; + } +} + +qint64 GLPlatform::glVersion() const +{ + return m_glVersion; +} + +qint64 GLPlatform::glslVersion() const +{ + return m_glslVersion; +} + +qint64 GLPlatform::mesaVersion() const +{ + return m_mesaVersion; +} + +qint64 GLPlatform::galliumVersion() const +{ + return m_galliumVersion; +} + +qint64 GLPlatform::serverVersion() const +{ + return m_serverVersion; +} + +qint64 GLPlatform::kernelVersion() const +{ + return m_kernelVersion; +} + +qint64 GLPlatform::driverVersion() const +{ + if (isMesaDriver()) + return mesaVersion(); + + return m_driverVersion; +} + +Driver GLPlatform::driver() const +{ + return m_driver; +} + +ChipClass GLPlatform::chipClass() const +{ + return m_chipClass; +} + +bool GLPlatform::isMesaDriver() const +{ + return mesaVersion() > 0; +} + +bool GLPlatform::isGalliumDriver() const +{ + return galliumVersion() > 0; +} + +bool GLPlatform::isRadeon() const +{ + return m_chipClass >= R100 && m_chipClass <= UnknownRadeon; +} + +bool GLPlatform::isNvidia() const +{ + return m_chipClass >= NV10 && m_chipClass <= UnknownNVidia; +} + +bool GLPlatform::isIntel() const +{ + return m_chipClass >= I8XX && m_chipClass <= UnknownIntel; +} + +bool GLPlatform::isVirtualBox() const +{ + return m_driver == Driver_VirtualBox; +} + +bool GLPlatform::isVMware() const +{ + return m_driver == Driver_VMware; +} + +bool GLPlatform::isSoftwareEmulation() const +{ + return m_driver == Driver_Softpipe || m_driver == Driver_Swrast || m_driver == Driver_Llvmpipe; +} + +bool GLPlatform::isAdreno() const +{ + return m_chipClass >= Adreno1XX && m_chipClass <= UnknownAdreno; +} + +const QByteArray &GLPlatform::glRendererString() const +{ + return m_renderer; +} + +const QByteArray &GLPlatform::glVendorString() const +{ + return m_vendor; +} + +const QByteArray &GLPlatform::glVersionString() const +{ + return m_version; +} + +const QByteArray &GLPlatform::glShadingLanguageVersionString() const +{ + return m_glsl_version; +} + +bool GLPlatform::isLooseBinding() const +{ + return m_looseBinding; +} + +bool GLPlatform::isVirtualMachine() const +{ + return m_virtualMachine; +} + +bool GLPlatform::preferBufferSubData() const +{ + return m_preferBufferSubData; +} + +bool GLPlatform::isGLES() const +{ + return m_gles; +} + +void GLPlatform::cleanup() +{ + delete s_platform; + s_platform = nullptr; +} + +} // namespace KWin + diff --git a/greeter/seccomp_filter.cpp b/greeter/seccomp_filter.cpp --- a/greeter/seccomp_filter.cpp +++ b/greeter/seccomp_filter.cpp @@ -21,9 +21,11 @@ along with this program. If not, see . *********************************************************************/ #include "seccomp_filter.h" +#include "kwinglplatform.h" #include #include +#include #include #include @@ -38,7 +40,22 @@ { // trigger OpenGL context creation // we need this to ensure that all required files are opened for write - QOpenGLContext::supportsThreadedOpenGL(); + // on NVIDIA we need to keep write around, otherwise BUG 384005 happens + bool writeSupported = true; + QScopedPointer dummySurface(new QOffscreenSurface); + dummySurface->create(); + QOpenGLContext dummyGlContext; + if (dummyGlContext.create()) { + if (dummyGlContext.makeCurrent(dummySurface.data())) { + auto gl = KWin::GLPlatform::instance(); + gl->detect(); + gl->printResults(); + if (gl->driver() == KWin::Driver_NVidia) { + // BUG: 384005 + writeSupported = false; + } + } + } // access DBus to have the socket open QDBusConnection::sessionBus(); @@ -57,8 +74,10 @@ // instead disallow opening new files for writing // they should fail with EPERM error - seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY)); - seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR)); + if (writeSupported) { + seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY)); + seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR)); + } seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT)); // disallow going to a socket