kscreenlocker/0001-Don-t-dissallow-open-with-write-flag-syscall-on-NVID.patch
2017-08-31 07:35:10 +00:00

1641 lines
49 KiB
Diff

From abe0c021226e5bf97ac61171941e75a75becf0e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Fl=C3=B6ser?= <mgraesslin@kde.org>
Date: Thu, 31 Aug 2017 09:10:28 +0200
Subject: [PATCH] Don't dissallow open with write flag syscall on NVIDIA
Summary:
The latest NVIDIA driver crashes the greeter due to our seccomp enabled
sandbox being too restrictive. The driver is now opening files for
writing after our dummy context got created and this causes a crash. In
order to provide our users a working system again we better disable the
seccomp rule for NVIDIA users for the time being.
To detect whether an NVIDIA driver is used I copied the glplatform from
KWin which is known to work and more reliable than writing new custom
code even if it's a code copy. For master I'll look into splitting that
one out from KWin and putting it into a dedicated library so that we can
link it.
This of course means that the seccomp based sandbox is now incomplete
for NVIDIA users. An idea is to add an additional apparmor rule in
master to enforce the write restrictions in similar way without forcing
it for /dev.
BUG: 384005
Test Plan: I don't have an NVIDIA
Reviewers: #plasma, davidedmundson
Reviewed By: #plasma, davidedmundson
Subscribers: davidedmundson, jriddell, plasma-devel
Tags: #plasma
Differential Revision: https://phabricator.kde.org/D7616
---
greeter/CMakeLists.txt | 1 +
greeter/autotests/CMakeLists.txt | 2 +-
greeter/autotests/seccomp_test.cpp | 4 +
greeter/kwinglplatform.cpp | 1062 ++++++++++++++++++++++++++++++++++++
greeter/kwinglplatform.h | 416 ++++++++++++++
greeter/seccomp_filter.cpp | 25 +-
6 files changed, 1506 insertions(+), 4 deletions(-)
create mode 100644 greeter/kwinglplatform.cpp
create mode 100644 greeter/kwinglplatform.h
diff --git a/greeter/CMakeLists.txt b/greeter/CMakeLists.txt
index c5b639a..49836b8 100644
--- a/greeter/CMakeLists.txt
+++ b/greeter/CMakeLists.txt
@@ -13,6 +13,7 @@ set(kscreenlocker_greet_SRCS
main.cpp
noaccessnetworkaccessmanagerfactory.cpp
wallpaper_integration.cpp
+ kwinglplatform.cpp
)
if(HAVE_SECCOMP)
diff --git a/greeter/autotests/CMakeLists.txt b/greeter/autotests/CMakeLists.txt
index d5cb23d..d44f308 100644
--- a/greeter/autotests/CMakeLists.txt
+++ b/greeter/autotests/CMakeLists.txt
@@ -32,7 +32,7 @@ target_link_libraries(killTest Qt5::Test)
# 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
index 0006140..571c79d 100644
--- a/greeter/autotests/seccomp_test.cpp
+++ b/greeter/autotests/seccomp_test.cpp
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include <config-kscreenlocker.h>
#include "../seccomp_filter.h"
+#include "../kwinglplatform.h"
#include <QtTest/QtTest>
#include <QTemporaryFile>
@@ -55,6 +56,9 @@ void SeccompTest::testCreateFile()
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.cpp b/greeter/kwinglplatform.cpp
new file mode 100644
index 0000000..ee98b50
--- /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 <fredrik@kde.org>
+
+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 <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#include "kwinglplatform.h"
+
+#include <QRegExp>
+#include <QStringList>
+#include <QDebug>
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+
+#include <sys/utsname.h>
+
+#include <iostream>
+#include <iomanip>
+#include <ios>
+
+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<QByteArray> 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<QByteArray> 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 OpenGL<space>ES<space><version number><space><vendor-specific information>."
+ // 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<QByteArray>::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<QByteArray> 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<QByteArray> 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/kwinglplatform.h b/greeter/kwinglplatform.h
new file mode 100644
index 0000000..60bebe1
--- /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 <fredrik@kde.org>
+
+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 <http://www.gnu.org/licenses/>.
+*********************************************************************/
+
+#ifndef KWIN_GLPLATFORM_H
+#define KWIN_GLPLATFORM_H
+
+#include <QByteArray>
+#include <QSet>
+
+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<QByteArray> 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/seccomp_filter.cpp b/greeter/seccomp_filter.cpp
index 79244f9..9d61d4a 100644
--- a/greeter/seccomp_filter.cpp
+++ b/greeter/seccomp_filter.cpp
@@ -21,9 +21,11 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "seccomp_filter.h"
+#include "kwinglplatform.h"
#include <QDBusConnection>
#include <QOpenGLContext>
+#include <QOffscreenSurface>
#include <seccomp.h>
#include <sys/socket.h>
@@ -38,7 +40,22 @@ void init()
{
// 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<QOffscreenSurface> 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 @@ void init()
// 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
--
2.14.1