summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/linux/drm
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/linux/drm')
-rw-r--r--src/VBox/Additions/linux/drm/.scm-settings35
-rw-r--r--src/VBox/Additions/linux/drm/Makefile.kmk52
-rw-r--r--src/VBox/Additions/linux/drm/Makefile.module.kms72
-rw-r--r--src/VBox/Additions/linux/drm/README.testing13
-rwxr-xr-xsrc/VBox/Additions/linux/drm/files_vboxvideo_drv59
-rw-r--r--src/VBox/Additions/linux/drm/indent.sed285
-rw-r--r--src/VBox/Additions/linux/drm/vbox_drv.c455
-rw-r--r--src/VBox/Additions/linux/drm/vbox_drv.h557
-rw-r--r--src/VBox/Additions/linux/drm/vbox_fb.c541
-rw-r--r--src/VBox/Additions/linux/drm/vbox_hgsmi.c130
-rw-r--r--src/VBox/Additions/linux/drm/vbox_irq.c225
-rw-r--r--src/VBox/Additions/linux/drm/vbox_main.c704
-rw-r--r--src/VBox/Additions/linux/drm/vbox_mode.c949
-rw-r--r--src/VBox/Additions/linux/drm/vbox_prime.c82
-rw-r--r--src/VBox/Additions/linux/drm/vbox_ttm.c843
15 files changed, 5002 insertions, 0 deletions
diff --git a/src/VBox/Additions/linux/drm/.scm-settings b/src/VBox/Additions/linux/drm/.scm-settings
new file mode 100644
index 00000000..8e7c51aa
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/.scm-settings
@@ -0,0 +1,35 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for linux drm driver.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.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, in version 3 of the
+# License.
+#
+# 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 <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+
+#Sources are MIT licensed for simplier upstreaming, several files are external.
+/*.c|/*.h: --license-based-on-mit --no-convert-tabs
+/vbox_hgsmi.c: --license-mit
+/Makefile.module.kms: --treat-as Makefile
+
+--filter-out-files /README.testing
+
diff --git a/src/VBox/Additions/linux/drm/Makefile.kmk b/src/VBox/Additions/linux/drm/Makefile.kmk
new file mode 100644
index 00000000..82e9e297
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/Makefile.kmk
@@ -0,0 +1,52 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the vboxvideo DRM module (linux kernel OpenGL module).
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.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, in version 3 of the
+# License.
+#
+# 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 <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Populate FILES_VBOXVIDEO_DRM_NOBIN
+#
+INSTALLS += vboxvideo-src
+include $(PATH_SUB_CURRENT)/files_vboxvideo_drv
+vboxvideo-src_DEPS = \
+ $(PATH_ROOT)/src/VBox/Additions/linux/drm/files_vboxvideo_drv \
+ $(PATH_ROOT)/src/VBox/Additions/linux/drm/indent.sed
+vboxvideo-src_INST = $(INST_ADDITIONS)src/vboxvideo/
+vboxvideo-src_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXVIDEO_DRM_NOBIN))
+vboxvideo-src_EXEC_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXVIDEO_DRM_BIN))
+vboxvideo-src_INSTALLER = $(RM_EXT) -f -- "$2" && \
+ $(if $(filter %.c %.h,$2),$(SED) -f $(PATH_ROOT)/src/VBox/Additions/linux/drm/indent.sed \
+ --output "$2" "$1",$(CP_EXT) "$1" "$2") && \
+ $(CHMOD_EXT) "$(if $(mode),$(mode),0644)" "$2"
+
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/linux/drm/Makefile.module.kms b/src/VBox/Additions/linux/drm/Makefile.module.kms
new file mode 100644
index 00000000..99f4dab3
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/Makefile.module.kms
@@ -0,0 +1,72 @@
+# $Id: Makefile.module.kms $
+## @file
+# VirtualBox Guest Additions Module Makefile.
+#
+# (For 2.6.x this file must be 'Makefile'!)
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.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, in version 3 of the
+# License.
+#
+# 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 <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile-header.gmk
+VBOXDRM_DIR = $(VBOX_MODULE_SRC_DIR)
+
+# We want to build on Linux 3.11 and later and on all EL 7 kernels.
+VBOX_BUILD =
+ifneq ($(filter-out 1.% 2.% 3.0 3.0.% 3.1 3.1.% 3.2 3.2.% 3.3 3.3.% 3.4 3.4.% 3.5 3.5.% 3.6 3.6.% 3.7 3.7.% 3.8 3.8.% 3.9 3.9.% 3.10 3.10.%,$(KERN_VER)),)
+ VBOX_BUILD = 1
+endif
+ifeq ($(filter-out %.el7.x86_64,$(KERN_VER)),)
+ VBOX_BUILD = 1
+endif
+
+ifneq ($(VBOX_BUILD),)
+
+VBOXMOD_NAME = vboxvideo
+VBOXMOD_OBJS = \
+ hgsmi_base.o \
+ modesetting.o \
+ vbox_drv.o \
+ vbox_fb.o \
+ vbox_irq.o \
+ vbox_main.o \
+ vbox_mode.o \
+ vbox_ttm.o \
+ vbva_base.o \
+ vbox_prime.o \
+ vbox_hgsmi.o
+VBOXMOD_INCL = \
+ $(VBOXDRM_DIR) \
+ $(KERN_INCL)/drm
+
+include $(obj)/Makefile-footer.gmk
+
+else # !VBOX_BUILD
+
+ all:
+ install:
+ clean:
+
+endif # !VBOX_BUILD
+
diff --git a/src/VBox/Additions/linux/drm/README.testing b/src/VBox/Additions/linux/drm/README.testing
new file mode 100644
index 00000000..72cd96c3
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/README.testing
@@ -0,0 +1,13 @@
+This document lists things which have been known to fail in the past with the
+drm video driver and which should be tested regularly, e.g. when making code
+changes or before releases.
+
+ * Test that "auto-resize" is enabled in the GUI if user space supports it.
+ * Test that old versions of Plymouth which do not report rectangles
+ (pre-0.9.0/2014-05-20) update the screen correctly.
+ * Having valid, non-overlapping offset hints on all screens has caused
+ mouse integration and/or screen updates to break for fbdev, X.Org or
+ GNOME Shell/Wayland.
+ * Note that if a multi-screen VM is booted with only one screen enabled
+ the fbdev console will be disabled on the others until reboot. Test this
+ configuration.
diff --git a/src/VBox/Additions/linux/drm/files_vboxvideo_drv b/src/VBox/Additions/linux/drm/files_vboxvideo_drv
new file mode 100755
index 00000000..04092484
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/files_vboxvideo_drv
@@ -0,0 +1,59 @@
+#!/bin/sh
+# $Id: files_vboxvideo_drv $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2011-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.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, in version 3 of the
+# License.
+#
+# 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 <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+FILES_VBOXVIDEO_DRM_NOBIN=" \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/revision-generated.h=>revision-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+ ${PATH_ROOT}/include/VBox/Graphics/VBoxVideo.h=>vboxvideo.h \
+ ${PATH_ROOT}/include/VBox/Graphics/VBoxVideoGuest.h=>vboxvideo_guest.h \
+ ${PATH_ROOT}/include/VBox/Graphics/HGSMIChannels.h=>hgsmi_channels.h \
+ ${PATH_ROOT}/include/VBox/Graphics/HGSMIChSetup.h=>hgsmi_ch_setup.h \
+ ${PATH_ROOT}/include/VBox/Graphics/HGSMIContext.h=>hgsmi_context.h \
+ ${PATH_ROOT}/include/VBox/Graphics/HGSMIDefs.h=>hgsmi_defs.h \
+ ${PATH_ROOT}/include/VBox/Graphics/VBoxVideoErr.h=>vbox_err.h \
+ ${PATH_ROOT}/include/VBox/Graphics/VBoxVideoVBE.h=>vboxvideo_vbe.h \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp=>hgsmi_base.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp=>modesetting.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp=>vbva_base.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_drv.c=>vbox_drv.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_drv.h=>vbox_drv.h \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_fb.c=>vbox_fb.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_irq.c=>vbox_irq.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_main.c=>vbox_main.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_mode.c=>vbox_mode.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_prime.c=>vbox_prime.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_ttm.c=>vbox_ttm.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/vbox_hgsmi.c=>vbox_hgsmi.c \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk \
+ ${PATH_ROOT}/src/VBox/Additions/linux/drm/Makefile.module.kms=>Makefile \
+"
+
+FILES_VBOXVIDEO_DRM_BIN=" \
+"
diff --git a/src/VBox/Additions/linux/drm/indent.sed b/src/VBox/Additions/linux/drm/indent.sed
new file mode 100644
index 00000000..4401ca44
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/indent.sed
@@ -0,0 +1,285 @@
+# Oracle VM VirtualBox
+# VirtualBox to Linux kernel coding style conversion script.
+
+#
+# Copyright (C) 2017-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.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, in version 3 of the
+# License.
+#
+# 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 <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+# This script is for converting code inside the vboxvideo module to Linux
+# kernel coding style. It assumes correct VirtualBox coding style, will break
+# break if the coding style is wrong (e.g. tab instead of spaces) and is not
+# indended to be a generic solution: for example, identifiers will be
+# translated case by case, not algorithmically. It also assumes that any
+# flexibility in either coding style will be used where possible to make the
+# code conform to both at once.
+
+# Replace up to six leading groups of four spaces with tabs.
+s/^ */\t\t\t\t\t\t/g
+s/^ /\t\t\t\t\t/g
+s/^ /\t\t\t\t/g
+s/^ /\t\t\t/g
+s/^ /\t\t/g
+s/^ /\t/g
+
+# Change various symbols and file names to fit kernel conventions.
+
+# Miscellaneous:
+# Remove @file headers.
+\|/\*\* @file| {
+:start
+ N
+ s|\*/|\*/|g
+ T start
+ N
+ d
+}
+/^\/\* \$Id:.*\*\/$/d
+/^typedef .* HGSMIOFFSET;/d
+/^typedef .* HGSMISIZE;/d
+s/^#\( *\)include <\([^/]*\)>/#\1include "\2"/g
+
+# File names:
+s/\bHGSMIBase\.h\b/vbox_drv.h/g
+s/\bHGSMIChannels\.h\b/hgsmi_channels.h/g
+s/\bHGSMIChSetup\.h\b/hgsmi_ch_setup.h/g
+s/\bHGSMIContext\.h\b/hgsmi_context.h/g
+s/\bHGSMIDefs\.h\b/hgsmi_defs.h/g
+s/\bVBoxVideoGuest\.h\b/vboxvideo_guest.h/g
+s/\bVBoxVideo\.h\b/vboxvideo.h/g
+s/\bVBoxVideoIPRT\.h\b/vbox_err.h/g
+s/\bVBoxVideoVBE\.h\b/vboxvideo_vbe.h/g
+
+# Function names:
+s/\btestQueryConf\b/hgsmi_test_query_conf/g
+s/\bVBoxHGSMIBufferAlloc\b/hgsmi_buffer_alloc/g
+s/\bVBoxHGSMIBufferFree\b/hgsmi_buffer_free/g
+s/\bVBoxHGSMIBufferSubmit\b/hgsmi_buffer_submit/g
+s/\bVBoxHGSMICursorPosition\b/hgsmi_cursor_position/g
+s/\bVBoxHGSMIGetModeHints\b/hgsmi_get_mode_hints/g
+s/\bVBoxHGSMIProcessDisplayInfo\b/hgsmi_process_display_info/g
+s/\bVBoxHGSMIReportFlagsLocation\b/hgsmi_report_flags_location/g
+s/\bVBoxHGSMISendCapsInfo\b/hgsmi_send_caps_info/g
+s/\bVBoxHGSMIUpdateInputMapping\b/hgsmi_update_input_mapping/g
+s/\bVBoxHGSMIUpdatePointerShape\b/hgsmi_update_pointer_shape/g
+s/\bvboxHwBufferAvail\b/vbva_buffer_available/g
+s/\bvboxHwBufferEndUpdate\b/vbva_buffer_end_update/g
+s/\bvboxHwBufferFlush\b/vbva_buffer_flush/g
+s/\bvboxHwBufferPlaceDataAt\b/vbva_buffer_place_data_at/g
+s/\bvboxHwBufferWrite\b/vbva_write/g
+s/\bVBoxQueryConfHGSMI\b/hgsmi_query_conf/g
+s/\bVBoxVBVABufferBeginUpdate\b/vbva_buffer_begin_update/g
+s/\bVBoxVBVABufferEndUpdate\b/vbva_buffer_end_update/g
+s/\bVBoxVBVADisable\b/vbva_disable/g
+s/\bVBoxVBVAEnable\b/vbva_enable/g
+s/\bvboxVBVAInformHost\b/vbva_inform_host/g
+s/\bvboxVBVASetupBufferContext\b/vbva_setup_buffer_context/g
+s/\bVBVO_PORT_READ_U8\b/inb/g
+s/\bVBVO_PORT_READ_U16\b/inw/g
+s/\bVBVO_PORT_READ_U32\b/inl/g
+s/\bVBVO_PORT_WRITE_U8\b *( *\(\b[^(),]*\b\) *, *\(\b[^(),]*\b\) *)/outb(\2, \1)/g
+s/\bVBVO_PORT_WRITE_U16\b *( *\(\b[^(),]*\b\) *, *\(\b[^(),]*\b\) *)/outw(\2, \1)/g
+s/\bVBVO_PORT_WRITE_U32\b *( *\(\b[^(),]*\b\) *, *\(\b[^(),]*\b\) *)/outl(\2, \1)/g
+s/\bVBVO_PORT_WRITE_U[0-9]*\b/VBVO_PORT_WRITE_statement_should_be_on_one_line/g
+
+# Macros:
+s/\b_1K\b/1024/g
+s/\b_4M\b/4*1024*1024/g
+s/\bAssert\b\([^;]*\);/WARN_ON_ONCE(!(\1));/g
+s/\bAssertCompile\b/assert_compile/g
+s/\bAssertCompileSize\b/assert_compile_size/g
+s/\bAssertPtr\b\([^;]*\);/WARN_ON_ONCE(!(\1));/g
+s/\bAssertPtrReturn\b/assert_ptr_return/g
+/AssertPtrNullReturnVoid/d
+s/\bAssertRC\b\([^;]*\);/WARN_ON_ONCE(RT_FAILURE\1);/g
+s/\bAssertRC\b/Assert_RC_statement_should_be_on_one_line/g
+s/\bDECLCALLBACK\b(\([^)]*\))/\1/g
+ s/\bDECLCALLBACKTYPE\b(\([^,)]*\), *\([^,)]*\), *(\([^;)]*\) *) *)/\1 \2(\3)/g
+s/\bDECLCALLBACKMEMBER\b(\([^,)]*\), *\([^,)]*\), *(\([^;)]*\) *) *)/\1 (*\2)(\3)/g
+s/^\bDECLHIDDEN\b(\([^)]*\))/\1/g
+s/\bDECLINLINE\b(\([^)]*\))/static inline \1/g
+s/\bRT_BIT\b/BIT/g
+s/\bRT_BOOL\b(\([^)]*\))/(!!(\1))/g
+/RT_C_DECLS/d
+s/\bUINT16_MAX\b/U16_MAX/g
+s/\bUINT32_MAX\b/U32_MAX/g
+s/\bUINT32_C\b(\(.*\))/\1u/g
+s/!RT_VALID_PTR(/WARN_ON(!/g
+s/\bRT_UNTRUSTED_VOLATILE_HOST\b//g
+s/\bRT_UNTRUSTED_VOLATILE_GUEST\b//g
+s/\bRT_UNTRUSTED_VOLATILE_HSTGST\b//g
+
+# Type names:
+s/\bint32_t\b/s32/g
+s/\buint8_t\b/u8/g
+s/\buint16_t\b/u16/g
+s/\buint32_t\b/u32/g
+s/(HGSMIBUFFERLOCATION \*)//g # Remove C++ casts from void.
+s/typedef struct HGSMIBUFFERLOCATION/struct hgsmi_buffer_location/g
+s/struct HGSMIBUFFERLOCATION/struct hgsmi_buffer_location/g
+s/} HGSMIBUFFERLOCATION/}/g
+s/\bHGSMIBUFFERLOCATION\b/struct hgsmi_buffer_location/g
+s/\([^*] *\)\bPHGSMIGUESTCOMMANDCONTEXT\b/\1struct gen_pool */g
+s/(HGSMIHOSTFLAGS \*)//g # Remove C++ casts from void.
+s/typedef struct HGSMIHOSTFLAGS/struct hgsmi_host_flags/g
+s/struct HGSMIHOSTFLAGS/struct hgsmi_host_flags/g
+s/} HGSMIHOSTFLAGS/}/g
+s/\bHGSMIHOSTFLAGS\b/struct hgsmi_host_flags/g
+s/\bHGSMIOFFSET\b/u32/g
+s/\bHGSMISIZE\b/u32/g
+s/\bRTRECT\b/void/g
+s/(VBVABUFFERCONTEXT \*)//g # Remove C++ casts from void.
+s/struct VBVABUFFERCONTEXT/struct vbva_buf_context/g
+s/} VBVABUFFERCONTEXT/} vbva_buf_context/g
+s/\bVBVABUFFERCONTEXT\b/struct vbva_buf_context/g
+s/\([^*] *\)\bPVBVABUFFERCONTEXT\b/\1struct vbva_buf_context */g
+s/(VBVACAPS \*)//g # Remove C++ casts from void.
+s/struct VBVACAPS/struct vbva_caps/g
+s/} VBVACAPS/} vbva_caps/g
+s/\bVBVACAPS\b/struct vbva_caps/g
+s/(VBVACONF32 \*)//g # Remove C++ casts from void.
+s/struct VBVACONF32/struct vbva_conf32/g
+s/} VBVACONF32/} vbva_conf32/g
+s/\bVBVACONF32\b/struct vbva_conf32/g
+s/(VBVACURSORPOSITION \*)//g # Remove C++ casts from void.
+s/struct VBVACURSORPOSITION/struct vbva_cursor_position/g
+s/} VBVACURSORPOSITION/} vbva_cursor_position/g
+s/\bVBVACURSORPOSITION\b/struct vbva_cursor_position/g
+s/(VBVAENABLE_EX \*)//g # Remove C++ casts from void.
+s/struct VBVAENABLE_EX/struct vbva_enable_ex/g
+s/} VBVAENABLE_EX/} vbva_enable_ex/g
+s/\bVBVAENABLE_EX\b/struct vbva_enable_ex/g
+s/(VBVAMOUSEPOINTERSHAPE \*)//g # Remove C++ casts from void.
+s/struct VBVAMOUSEPOINTERSHAPE/struct vbva_mouse_pointer_shape/g
+s/} VBVAMOUSEPOINTERSHAPE/} vbva_mouse_pointer_shape/g
+s/\bVBVAMOUSEPOINTERSHAPE\b/struct vbva_mouse_pointer_shape/g
+s/(VBVAMODEHINT \*)//g # Remove C++ casts from void.
+s/struct VBVAMODEHINT/struct vbva_modehint/g
+s/} VBVAMODEHINT/} vbva_modehint/g
+s/\bVBVAMODEHINT\b/struct vbva_modehint/g
+s/(VBVAQUERYMODEHINTS \*)//g # Remove C++ casts from void.
+s/struct VBVAQUERYMODEHINTS/struct vbva_query_mode_hints/g
+s/} VBVAQUERYMODEHINTS/} vbva_query_mode_hints/g
+s/\bVBVAQUERYMODEHINTS\b/struct vbva_query_mode_hints/g
+s/(VBVAREPORTINPUTMAPPING \*)//g # Remove C++ casts from void.
+s/struct VBVAREPORTINPUTMAPPING/struct vbva_report_input_mapping/g
+s/} VBVAREPORTINPUTMAPPING/} vbva_report_input_mapping/g
+s/\bVBVAREPORTINPUTMAPPING\b/struct vbva_report_input_mapping/g
+
+# Variable and parameter names:
+s/\baRecords\b/records/g
+s/\bau8Data\b/data/g
+s/\bau32Reserved\b/reserved/g
+s/\bBase\b/base/g
+s/\bbEnable\b/enable/g
+s/\bbRc\b/ret/g
+s/\bcb\b/len/g
+s/\bcbBuffer\b/buffer_length/g
+s/\bcbChunk\b/chunk/g
+s/\bcbData\b/data_len/g
+s/\bcbHintsStructureGuest\b/hints_structure_guest_size/g
+s/\bcbHwBufferAvail\b/available/g
+s/\bcbLength\b/len/g
+s/\bcbLocation\b/buf_len/g
+s/\bcbPartialWriteThreshold\b/partial_write_tresh/g ## @todo fix this?
+s/\bcbPitch\b/pitch/g
+s/\bcbPixels\b/pixel_len/g
+s/\bcBPP\b/bpp/g
+s/\bcbRecord\b/len_and_flags/g ## @todo fix this?
+s/\bcDisplay\b/display/g
+s/\bcHeight\b/height/g
+s/\bcHintsQueried\b/hints_queried_count/g
+s/\bcHotX\b/hot_x/g
+s/\bcHotY\b/hot_y/g
+s/\bcOriginX\b/origin_x/g
+s/\bcOriginY\b/origin_y/g
+s/\bcScreen\b/screen/g
+s/\bcScreens\b/screens/g
+s/\bcWidth\b/width/g
+s/\bfCaps\b/caps/g
+s/\bfFlags\b/flags/g
+s/\bfHwBufferOverflow\b/buffer_overflow/g
+s/\bfReportPosition\b/report_position/g
+s/\bfu32Flags\b/flags/g
+s/\bhostFlags\b/host_flags/g
+s/\bi32Diff\b/diff/g
+s/\bi32OriginX\b/origin_x/g
+s/\bi32OriginY\b/origin_y/g
+s/\bi32Result\b/result/g
+s/\bindexRecordFirst\b/first_record_index/g
+s/\bindexRecordFree\b/free_record_index/g
+s/\bindexRecordNext\b/next/g
+s/\boff32Data\b/data_offset/g
+s/\boff32Free\b/free_offset/g
+s/\boffLocation\b/location/g
+s/\boffStart\b/start_offset/g
+s/\boffVRAMBuffer\b/buffer_offset/g
+s/\bpaHints\b/hints/g
+s/\bpCtx\b/ctx/g
+s/\bpPixels\b/pixels/g
+s/\bpRecord\b/record/g
+s/\bpulValue\b/value_ret/g
+s/\bpVBVA\b/vbva/g
+s/\bpxHost\b/x_host/g
+s/\bpyHost\b/y_host/g
+s/\bu16BitsPerPixel\b/bits_per_pixel/g
+s/\bu16Flags\b/flags/g
+s/\bu32BytesTillBoundary\b/bytes_till_boundary/g
+s/\bu32Flags\b/flags/g
+s/\bu32Height\b/height/g
+s/\bu32HostEvents\b/host_events/g
+s/\bu32HostFlags\b/host_flags/g
+s/\bu32HotX\b/hot_x/g
+s/\bu32HotY\b/hot_y/g
+s/\bu32Index\b/index/g
+s/\bu32LineSize\b/line_size/g
+s/\bu32Offset\b/offset/g
+s/\bu32Reserved\b/reserved/g
+s/\bu32ScreenId\b/screen_id/g
+s/\bu32StartOffset\b/start_offset/g
+s/\bu32SupportedOrders\b/supported_orders/g
+s/\bu32Value\b/value/g
+s/\bu32ViewIndex\b/view_index/g
+s/\bu32Width\b/width/g
+s/\bulValue\b/value/g
+
+# Header file guard:
+s/__HGSMIChannels_h__/__HGSMI_CHANNELS_H__/g
+s/VBOX_INCLUDED_Graphics_HGSMIChSetup_h/__HGSMI_CH_SETUP_H__/g
+
+# And move braces. This must be the last expression as it jumps to the next
+# line.
+/..*$/ {
+ N
+ s/^\([\t ][\t ]*\)} *\n[\t ]*else/\1} else/g
+ t continue_else
+ b try_brace
+:continue_else
+ N
+:try_brace
+ s/^\([\t ].*\)\n[\t ][\t ]*{/\1 {/g
+ s/^\([^#()]*\)\n[\t ]*{/\1 {/g
+ t done_brace
+ P
+ D
+:done_brace
+ p
+ d
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_drv.c b/src/VBox/Additions/linux/drm/vbox_drv.c
new file mode 100644
index 00000000..3e4cbc60
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_drv.c
@@ -0,0 +1,455 @@
+/* $Id: vbox_drv.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ * This file is based on ast_drv.c
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/vt_kern.h>
+
+#include "vbox_drv.h"
+
+#include <drm/drm_crtc_helper.h>
+#if RTLNX_VER_MIN(5,1,0) || RTLNX_RHEL_MAJ_PREREQ(8,1)
+# include <drm/drm_probe_helper.h>
+#endif
+
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+# include <drm/drm_aperture.h>
+#endif
+
+#include "version-generated.h"
+#include "revision-generated.h"
+
+/** Detect whether kernel mode setting is OFF. */
+#if defined(CONFIG_VGA_CONSOLE)
+# if RTLNX_VER_MIN(5,17,0) || RTLNX_RHEL_RANGE(8,7, 8,99) || RTLNX_RHEL_MIN(9,1) || RTLNX_SUSE_MAJ_PREREQ(15,5)
+# define VBOX_VIDEO_NOMODESET() drm_firmware_drivers_only() && vbox_modeset == -1
+# elif RTLNX_VER_MIN(4,7,0)
+# define VBOX_VIDEO_NOMODESET() vgacon_text_force() && vbox_modeset == -1
+# else /* < 4.7.0 */
+# define VBOX_VIDEO_NOMODESET() 0
+# endif /* < 4.7.0 */
+#else /* !CONFIG_VGA_CONSOLE */
+# define VBOX_VIDEO_NOMODESET() 0
+#endif /* !CONFIG_VGA_CONSOLE */
+
+static int vbox_modeset = -1;
+
+MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
+module_param_named(modeset, vbox_modeset, int, 0400);
+
+static struct drm_driver driver;
+
+static const struct pci_device_id pciidlist[] = {
+ { 0x80ee, 0xbeef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0, 0, 0},
+};
+MODULE_DEVICE_TABLE(pci, pciidlist);
+
+static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+#if RTLNX_VER_MIN(4,19,0) || RTLNX_RHEL_MIN(8,3)
+ struct drm_device *dev = NULL;
+ int ret = 0;
+
+# if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+# if RTLNX_VER_MIN(5,15,0) || RTLNX_RHEL_RANGE(8,7, 8,99) || RTLNX_RHEL_MIN(9,1) || RTLNX_SUSE_MAJ_PREREQ(15,4)
+ ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &driver);
+# else
+ ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, "vboxvideofb");
+# endif
+ if (ret)
+ {
+ printk("unable to remove conflicting framebuffer devices\n");
+ return ret;
+ }
+# endif /* >= 5.14. */
+
+ dev = drm_dev_alloc(&driver, &pdev->dev);
+ if (IS_ERR(dev)) {
+ ret = PTR_ERR(dev);
+ goto err_drv_alloc;
+ }
+# if RTLNX_VER_MAX(5,14,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
+ dev->pdev = pdev;
+# endif
+ pci_set_drvdata(pdev, dev);
+
+ ret = vbox_driver_load(dev);
+ if (ret)
+ goto err_vbox_driver_load;
+
+ ret = drm_dev_register(dev, 0);
+ if (ret)
+ goto err_drv_dev_register;
+ return ret;
+
+err_drv_dev_register:
+ vbox_driver_unload(dev);
+err_vbox_driver_load:
+ drm_dev_put(dev);
+err_drv_alloc:
+ return ret;
+#else /* < 4.19.0 || RHEL < 8.3 */
+ return drm_get_pci_dev(pdev, ent, &driver);
+#endif
+}
+
+static void vbox_pci_remove(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+
+#if RTLNX_VER_MAX(4,19,0)
+ drm_put_dev(dev);
+#else
+ drm_dev_unregister(dev);
+ vbox_driver_unload(dev);
+ drm_dev_put(dev);
+#endif
+}
+
+#if RTLNX_VER_MAX(4,9,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
+static void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
+ bool suspend)
+{
+ if (!fb_helper || !fb_helper->fbdev)
+ return;
+
+ console_lock();
+ fb_set_suspend(fb_helper->fbdev, suspend);
+ console_unlock();
+}
+#endif
+
+static int vbox_drm_freeze(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ drm_kms_helper_poll_disable(dev);
+
+ pci_save_state(VBOX_DRM_TO_PCI_DEV(dev));
+
+ drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, true);
+
+ return 0;
+}
+
+static int vbox_drm_thaw(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ drm_mode_config_reset(dev);
+ drm_helper_resume_force_mode(dev);
+ drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, false);
+
+ return 0;
+}
+
+static int vbox_drm_resume(struct drm_device *dev)
+{
+ int ret;
+
+ if (pci_enable_device(VBOX_DRM_TO_PCI_DEV(dev)))
+ return -EIO;
+
+ ret = vbox_drm_thaw(dev);
+ if (ret)
+ return ret;
+
+ drm_kms_helper_poll_enable(dev);
+
+ return 0;
+}
+
+static int vbox_pm_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+ int error;
+
+ error = vbox_drm_freeze(ddev);
+ if (error)
+ return error;
+
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+static int vbox_pm_resume(struct device *dev)
+{
+ struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+
+ return vbox_drm_resume(ddev);
+}
+
+static int vbox_pm_freeze(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+
+ if (!ddev || !ddev->dev_private)
+ return -ENODEV;
+
+ return vbox_drm_freeze(ddev);
+}
+
+static int vbox_pm_thaw(struct device *dev)
+{
+ struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+
+ return vbox_drm_thaw(ddev);
+}
+
+static int vbox_pm_poweroff(struct device *dev)
+{
+ struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+
+ return vbox_drm_freeze(ddev);
+}
+
+static const struct dev_pm_ops vbox_pm_ops = {
+ .suspend = vbox_pm_suspend,
+ .resume = vbox_pm_resume,
+ .freeze = vbox_pm_freeze,
+ .thaw = vbox_pm_thaw,
+ .poweroff = vbox_pm_poweroff,
+ .restore = vbox_pm_resume,
+};
+
+static struct pci_driver vbox_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pciidlist,
+ .probe = vbox_pci_probe,
+ .remove = vbox_pci_remove,
+ .driver.pm = &vbox_pm_ops,
+};
+
+#if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
+/* This works around a bug in X servers prior to 1.18.4, which sometimes
+ * submit more dirty rectangles than the kernel is willing to handle and
+ * then disable dirty rectangle handling altogether when they see the
+ * EINVAL error. I do not want the code to hang around forever, which is
+ * why I am limiting it to certain kernel versions. We can increase the
+ * limit if some distributions uses old X servers with new kernels. */
+long vbox_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ long rc = drm_ioctl(filp, cmd, arg);
+
+ if (cmd == DRM_IOCTL_MODE_DIRTYFB && rc == -EINVAL)
+ return -EOVERFLOW;
+
+ return rc;
+}
+#endif /* RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4) */
+
+static const struct file_operations vbox_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+#if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
+ .unlocked_ioctl = vbox_ioctl,
+#else
+ .unlocked_ioctl = drm_ioctl,
+#endif
+ .mmap = vbox_mmap,
+ .poll = drm_poll,
+#if RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,0)
+ .fasync = drm_fasync,
+#endif
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .read = drm_read,
+};
+
+#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+static void
+#else
+static int
+#endif
+vbox_master_set(struct drm_device *dev,
+ struct drm_file *file_priv, bool from_open)
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ /*
+ * We do not yet know whether the new owner can handle hotplug, so we
+ * do not advertise dynamic modes on the first query and send a
+ * tentative hotplug notification after that to see if they query again.
+ */
+ vbox->initial_mode_queried = false;
+
+ mutex_lock(&vbox->hw_mutex);
+ /* Start the refresh timer in case the user does not provide dirty
+ * rectangles. */
+ vbox->need_refresh_timer = true;
+ schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
+ mutex_unlock(&vbox->hw_mutex);
+
+#if RTLNX_VER_MAX(5,9,0) && !RTLNX_RHEL_MAJ_PREREQ(8,4) && !RTLNX_SUSE_MAJ_PREREQ(15,3)
+ return 0;
+#endif
+}
+
+#if RTLNX_VER_MAX(4,8,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
+static void vbox_master_drop(struct drm_device *dev,
+ struct drm_file *file_priv, bool from_release)
+#else
+static void vbox_master_drop(struct drm_device *dev, struct drm_file *file_priv)
+#endif
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ /* See vbox_master_set() */
+ vbox->initial_mode_queried = false;
+ vbox_report_caps(vbox);
+
+ mutex_lock(&vbox->hw_mutex);
+ vbox->need_refresh_timer = false;
+ mutex_unlock(&vbox->hw_mutex);
+}
+
+static struct drm_driver driver = {
+#if RTLNX_VER_MAX(5,4,0) && !RTLNX_RHEL_MAJ_PREREQ(8,3) && !RTLNX_SUSE_MAJ_PREREQ(15,3)
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ |
+# if RTLNX_VER_MAX(5,1,0) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
+ DRIVER_IRQ_SHARED |
+# endif
+ DRIVER_PRIME,
+#else /* >= 5.4.0 && RHEL >= 8.3 && SLES >= 15-SP3 */
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ,
+#endif /* < 5.4.0 */
+
+#if RTLNX_VER_MAX(4,19,0) && !RTLNX_RHEL_MAJ_PREREQ(8,3)
+ /* Legacy hooks, but still supported. */
+ .load = vbox_driver_load,
+ .unload = vbox_driver_unload,
+#endif
+ .lastclose = vbox_driver_lastclose,
+ .master_set = vbox_master_set,
+ .master_drop = vbox_master_drop,
+#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
+# if RTLNX_VER_MAX(4,14,0) && !RTLNX_RHEL_MAJ_PREREQ(7,5) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+ .set_busid = drm_pci_set_busid,
+# endif
+#endif
+
+ .fops = &vbox_fops,
+#if RTLNX_VER_MAX(5,15,0) && !RTLNX_RHEL_RANGE(8,7, 8,99) && !RTLNX_RHEL_MAJ_PREREQ(9,1) && !RTLNX_SUSE_MAJ_PREREQ(15,5)
+ .irq_handler = vbox_irq_handler,
+#endif
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+
+#if RTLNX_VER_MAX(4,7,0)
+ .gem_free_object = vbox_gem_free_object,
+#endif
+ .dumb_create = vbox_dumb_create,
+ .dumb_map_offset = vbox_dumb_mmap_offset,
+#if RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
+ .dumb_destroy = vbox_dumb_destroy,
+#elif RTLNX_VER_MAX(5,12,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ .dumb_destroy = drm_gem_dumb_destroy,
+#endif
+#if RTLNX_VER_MAX(6,6,0) && !RTLNX_RHEL_RANGE(9,4, 9,99)
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+#endif
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_import_sg_table = vbox_gem_prime_import_sg_table,
+#if RTLNX_VER_MAX(6,6,0) && !RTLNX_RHEL_RANGE(9,4, 9,99)
+ .gem_prime_mmap = vbox_gem_prime_mmap,
+#endif
+
+#if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ .dev_priv_size = 0,
+# if RTLNX_VER_MIN(4,7,0)
+ .gem_free_object_unlocked = vbox_gem_free_object,
+# endif
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_pin = vbox_gem_prime_pin,
+ .gem_prime_unpin = vbox_gem_prime_unpin,
+ .gem_prime_get_sg_table = vbox_gem_prime_get_sg_table,
+ .gem_prime_vmap = vbox_gem_prime_vmap,
+ .gem_prime_vunmap = vbox_gem_prime_vunmap,
+#endif
+};
+
+static int __init vbox_init(void)
+{
+ printk("vboxvideo: loading version " VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) "\n");
+ if (VBOX_VIDEO_NOMODESET())
+ {
+ printk("vboxvideo: kernel is running with *nomodeset* parameter,\n");
+ printk("vboxvideo: please consider either to remove it or load driver\n");
+ printk("vboxvideo: with parameter modeset=1, unloading\n");
+ return -EINVAL;
+ }
+
+ if (vbox_modeset == 0)
+ {
+ printk("vboxvideo: driver loaded with modeset=0 parameter, unloading\n");
+ return -EINVAL;
+ }
+
+#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
+ return pci_register_driver(&vbox_pci_driver);
+#else
+ return drm_pci_init(&driver, &vbox_pci_driver);
+#endif
+}
+
+static void __exit vbox_exit(void)
+{
+#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
+ pci_unregister_driver(&vbox_pci_driver);
+#else
+ drm_pci_exit(&driver, &vbox_pci_driver);
+#endif
+}
+
+module_init(vbox_init);
+module_exit(vbox_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV));
+#endif
diff --git a/src/VBox/Additions/linux/drm/vbox_drv.h b/src/VBox/Additions/linux/drm/vbox_drv.h
new file mode 100644
index 00000000..8eb9f83e
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_drv.h
@@ -0,0 +1,557 @@
+/* $Id: vbox_drv.h $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ * This file is based on ast_drv.h
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef GA_INCLUDED_SRC_linux_drm_vbox_drv_h
+#define GA_INCLUDED_SRC_linux_drm_vbox_drv_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <linux/version.h>
+
+/* iprt/linux/version.h copy - start */
+/** @def RTLNX_VER_MIN
+ * Evaluates to true if the linux kernel version is equal or higher to the
+ * one specfied. */
+#define RTLNX_VER_MIN(a_Major, a_Minor, a_Patch) \
+ (LINUX_VERSION_CODE >= KERNEL_VERSION(a_Major, a_Minor, a_Patch))
+
+/** @def RTLNX_VER_MAX
+ * Evaluates to true if the linux kernel version is less to the one specfied
+ * (exclusive). */
+#define RTLNX_VER_MAX(a_Major, a_Minor, a_Patch) \
+ (LINUX_VERSION_CODE < KERNEL_VERSION(a_Major, a_Minor, a_Patch))
+
+/** @def RTLNX_VER_RANGE
+ * Evaluates to true if the linux kernel version is equal or higher to the given
+ * minimum version and less (but not equal) to the maximum version (exclusive). */
+#define RTLNX_VER_RANGE(a_MajorMin, a_MinorMin, a_PatchMin, a_MajorMax, a_MinorMax, a_PatchMax) \
+ ( LINUX_VERSION_CODE >= KERNEL_VERSION(a_MajorMin, a_MinorMin, a_PatchMin) \
+ && LINUX_VERSION_CODE < KERNEL_VERSION(a_MajorMax, a_MinorMax, a_PatchMax) )
+
+
+/** @def RTLNX_RHEL_MIN
+ * Require a minium RedHat release.
+ * @param a_iMajor The major release number (RHEL_MAJOR).
+ * @param a_iMinor The minor release number (RHEL_MINOR).
+ * @sa RTLNX_RHEL_MAX, RTLNX_RHEL_RANGE, RTLNX_RHEL_MAJ_PREREQ
+ */
+#if defined(RHEL_MAJOR) && defined(RHEL_MINOR)
+# define RTLNX_RHEL_MIN(a_iMajor, a_iMinor) \
+ ((RHEL_MAJOR) > (a_iMajor) || ((RHEL_MAJOR) == (a_iMajor) && (RHEL_MINOR) >= (a_iMinor)))
+#else
+# define RTLNX_RHEL_MIN(a_iMajor, a_iMinor) (0)
+#endif
+
+/** @def RTLNX_RHEL_MAX
+ * Require a maximum RedHat release, true for all RHEL versions below it.
+ * @param a_iMajor The major release number (RHEL_MAJOR).
+ * @param a_iMinor The minor release number (RHEL_MINOR).
+ * @sa RTLNX_RHEL_MIN, RTLNX_RHEL_RANGE, RTLNX_RHEL_MAJ_PREREQ
+ */
+#if defined(RHEL_MAJOR) && defined(RHEL_MINOR)
+# define RTLNX_RHEL_MAX(a_iMajor, a_iMinor) \
+ ((RHEL_MAJOR) < (a_iMajor) || ((RHEL_MAJOR) == (a_iMajor) && (RHEL_MINOR) < (a_iMinor)))
+#else
+# define RTLNX_RHEL_MAX(a_iMajor, a_iMinor) (0)
+#endif
+
+/** @def RTLNX_RHEL_RANGE
+ * Check that it's a RedHat kernel in the given version range.
+ * The max version is exclusive, the minimum inclusive.
+ * @sa RTLNX_RHEL_MIN, RTLNX_RHEL_MAX, RTLNX_RHEL_MAJ_PREREQ
+ */
+#if defined(RHEL_MAJOR) && defined(RHEL_MINOR)
+# define RTLNX_RHEL_RANGE(a_iMajorMin, a_iMinorMin, a_iMajorMax, a_iMinorMax) \
+ (RTLNX_RHEL_MIN(a_iMajorMin, a_iMinorMin) && RTLNX_RHEL_MAX(a_iMajorMax, a_iMinorMax))
+#else
+# define RTLNX_RHEL_RANGE(a_iMajorMin, a_iMinorMin, a_iMajorMax, a_iMinorMax) (0)
+#endif
+
+/** @def RTLNX_RHEL_MAJ_PREREQ
+ * Require a minimum minor release number for the given RedHat release.
+ * @param a_iMajor RHEL_MAJOR must _equal_ this.
+ * @param a_iMinor RHEL_MINOR must be greater or equal to this.
+ * @sa RTLNX_RHEL_MIN, RTLNX_RHEL_MAX
+ */
+#if defined(RHEL_MAJOR) && defined(RHEL_MINOR)
+# define RTLNX_RHEL_MAJ_PREREQ(a_iMajor, a_iMinor) ((RHEL_MAJOR) == (a_iMajor) && (RHEL_MINOR) >= (a_iMinor))
+#else
+# define RTLNX_RHEL_MAJ_PREREQ(a_iMajor, a_iMinor) (0)
+#endif
+
+
+/** @def RTLNX_SUSE_MAJ_PREREQ
+ * Require a minimum minor release number for the given SUSE release.
+ * @param a_iMajor CONFIG_SUSE_VERSION must _equal_ this.
+ * @param a_iMinor CONFIG_SUSE_PATCHLEVEL must be greater or equal to this.
+ */
+#if defined(CONFIG_SUSE_VERSION) && defined(CONFIG_SUSE_PATCHLEVEL)
+# define RTLNX_SUSE_MAJ_PREREQ(a_iMajor, a_iMinor) ((CONFIG_SUSE_VERSION) == (a_iMajor) && (CONFIG_SUSE_PATCHLEVEL) >= (a_iMinor))
+#else
+# define RTLNX_SUSE_MAJ_PREREQ(a_iMajor, a_iMinor) (0)
+#endif
+/* iprt/linux/version.h copy - end */
+
+#if RTLNX_VER_MAX(4,5,0)
+# include <linux/types.h>
+# include <linux/spinlock_types.h>
+#endif
+
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+
+
+#if RTLNX_VER_MAX(3,14,0) || RTLNX_RHEL_MAJ_PREREQ(7,1)
+#define U8_MAX ((u8)~0U)
+#define S8_MAX ((s8)(U8_MAX>>1))
+#define S8_MIN ((s8)(-S8_MAX - 1))
+#define U16_MAX ((u16)~0U)
+#define S16_MAX ((s16)(U16_MAX>>1))
+#define S16_MIN ((s16)(-S16_MAX - 1))
+#define U32_MAX ((u32)~0U)
+#define S32_MAX ((s32)(U32_MAX>>1))
+#define S32_MIN ((s32)(-S32_MAX - 1))
+#define U64_MAX ((u64)~0ULL)
+#define S64_MAX ((s64)(U64_MAX>>1))
+#define S64_MIN ((s64)(-S64_MAX - 1))
+#endif
+
+#if RTLNX_VER_MIN(5,5,0) || RTLNX_RHEL_MIN(8,3) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+# include <drm/drm_file.h>
+# include <drm/drm_drv.h>
+# include <drm/drm_device.h>
+# include <drm/drm_ioctl.h>
+# include <drm/drm_fourcc.h>
+# if RTLNX_VER_MAX(5,15,0) && !RTLNX_RHEL_RANGE(8,7, 8,99) && !RTLNX_RHEL_MAJ_PREREQ(9,1) && !RTLNX_SUSE_MAJ_PREREQ(15,5)
+# include <drm/drm_irq.h>
+# endif
+# include <drm/drm_vblank.h>
+#else /* < 5.5.0 || RHEL < 8.3 || SLES < 15-SP3 */
+# include <drm/drmP.h>
+#endif
+#if RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+# include <drm/drm_encoder.h>
+#endif
+#include <drm/drm_fb_helper.h>
+#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
+# include <drm/drm_gem.h>
+#endif
+
+#if RTLNX_VER_MIN(6,3,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+# include <drm/ttm/ttm_bo.h>
+#else
+# include <drm/ttm/ttm_bo_api.h>
+# include <drm/ttm/ttm_bo_driver.h>
+#endif
+#include <drm/ttm/ttm_placement.h>
+#if RTLNX_VER_MAX(5,13,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
+# include <drm/ttm/ttm_memory.h>
+#endif
+#if RTLNX_VER_MAX(5,12,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+# include <drm/ttm/ttm_module.h>
+#endif
+#if RTLNX_VER_MIN(5,10,0)
+# include <drm/ttm/ttm_resource.h>
+#endif
+
+#if RTLNX_VER_MIN(6,0,0) || RTLNX_RHEL_RANGE(8,8, 8,99) || RTLNX_RHEL_MAJ_PREREQ(9,2) || RTLNX_SUSE_MAJ_PREREQ(15,5)
+# include <drm/drm_framebuffer.h>
+#endif
+
+#include "vboxvideo_guest.h"
+#include "vboxvideo_vbe.h"
+#include "hgsmi_ch_setup.h"
+
+#include "product-generated.h"
+
+#if RTLNX_VER_MAX(4,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,5)
+static inline void drm_gem_object_put_unlocked(struct drm_gem_object *obj)
+{
+ drm_gem_object_unreference_unlocked(obj);
+}
+#endif
+
+#if RTLNX_VER_MAX(4,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,5)
+static inline void drm_gem_object_put(struct drm_gem_object *obj)
+{
+ drm_gem_object_unreference(obj);
+}
+#endif
+
+#define DRIVER_AUTHOR VBOX_VENDOR
+
+#define DRIVER_NAME "vboxvideo"
+#define DRIVER_DESC VBOX_PRODUCT " Graphics Card"
+#define DRIVER_DATE "20130823"
+
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+#define VBOX_MAX_CURSOR_WIDTH 64
+#define VBOX_MAX_CURSOR_HEIGHT 64
+#define CURSOR_PIXEL_COUNT (VBOX_MAX_CURSOR_WIDTH * VBOX_MAX_CURSOR_HEIGHT)
+#define CURSOR_DATA_SIZE (CURSOR_PIXEL_COUNT * 4 + CURSOR_PIXEL_COUNT / 8)
+
+#define VBOX_MAX_SCREENS 32
+
+#define GUEST_HEAP_OFFSET(vbox) ((vbox)->full_vram_size - \
+ VBVA_ADAPTER_INFORMATION_SIZE)
+#define GUEST_HEAP_SIZE VBVA_ADAPTER_INFORMATION_SIZE
+#define GUEST_HEAP_USABLE_SIZE (VBVA_ADAPTER_INFORMATION_SIZE - \
+ sizeof(HGSMIHOSTFLAGS))
+#define HOST_FLAGS_OFFSET GUEST_HEAP_USABLE_SIZE
+
+/** Field "pdev" of struct drm_device was removed in 5.14. This macro
+ * transparently handles this change. Input argument is a pointer
+ * to struct drm_device. */
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+# define VBOX_DRM_TO_PCI_DEV(_dev) to_pci_dev(_dev->dev)
+#else
+# define VBOX_DRM_TO_PCI_DEV(_dev) _dev->pdev
+#endif
+
+/** Field "num_pages" of struct ttm_resource was renamed to "size" in 6.2 and
+ * now represents number of bytes. This macro handles this change. Input
+ * argument is a pointer to struct ttm_resource. */
+#if RTLNX_VER_MIN(6,2,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+# define VBOX_BO_RESOURCE_NUM_PAGES(_resource) PFN_UP(_resource->size)
+#else
+# define VBOX_BO_RESOURCE_NUM_PAGES(_resource) _resource->num_pages
+#endif
+
+/** How frequently we refresh if the guest is not providing dirty rectangles. */
+#define VBOX_REFRESH_PERIOD (HZ / 2)
+
+#if RTLNX_VER_MAX(3,13,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
+static inline void *devm_kcalloc(struct device *dev, size_t n, size_t size,
+ gfp_t flags)
+{
+ return devm_kzalloc(dev, n * size, flags);
+}
+#endif
+
+struct vbox_fbdev;
+
+struct vbox_private {
+ struct drm_device *dev;
+
+ u8 __iomem *guest_heap;
+ u8 __iomem *vbva_buffers;
+ struct gen_pool *guest_pool;
+ struct VBVABUFFERCONTEXT *vbva_info;
+ bool any_pitch;
+ u32 num_crtcs;
+ /** Amount of available VRAM, including space used for buffers. */
+ u32 full_vram_size;
+ /** Amount of available VRAM, not including space used for buffers. */
+ u32 available_vram_size;
+ /** Array of structures for receiving mode hints. */
+ VBVAMODEHINT *last_mode_hints;
+
+ struct vbox_fbdev *fbdev;
+
+ int fb_mtrr;
+
+ struct {
+#if RTLNX_VER_MAX(5,0,0) && !RTLNX_RHEL_MAJ_PREREQ(7,7) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
+ struct drm_global_reference mem_global_ref;
+ struct ttm_bo_global_ref bo_global_ref;
+#endif
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ struct ttm_device bdev;
+#else
+ struct ttm_bo_device bdev;
+#endif
+ bool mm_initialised;
+ } ttm;
+
+ struct mutex hw_mutex; /* protects modeset and accel/vbva accesses */
+ /**
+ * We decide whether or not user-space supports display hot-plug
+ * depending on whether they react to a hot-plug event after the initial
+ * mode query.
+ */
+ bool initial_mode_queried;
+ /**
+ * Do we know that the current user can send us dirty rectangle information?
+ * If not, do periodic refreshes until we do know.
+ */
+ bool need_refresh_timer;
+ /**
+ * As long as the user is not sending us dirty rectangle information,
+ * refresh the whole screen at regular intervals.
+ */
+ struct delayed_work refresh_work;
+ struct work_struct hotplug_work;
+ u32 input_mapping_width;
+ u32 input_mapping_height;
+ /**
+ * Is user-space using an X.Org-style layout of one large frame-buffer
+ * encompassing all screen ones or is the fbdev console active?
+ */
+ bool single_framebuffer;
+ u32 cursor_width;
+ u32 cursor_height;
+ u32 cursor_hot_x;
+ u32 cursor_hot_y;
+ size_t cursor_data_size;
+ u8 cursor_data[CURSOR_DATA_SIZE];
+};
+
+#undef CURSOR_PIXEL_COUNT
+#undef CURSOR_DATA_SIZE
+
+#if RTLNX_VER_MIN(4,19,0) || RTLNX_RHEL_MIN(8,3)
+int vbox_driver_load(struct drm_device *dev);
+#else
+int vbox_driver_load(struct drm_device *dev, unsigned long flags);
+#endif
+#if RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+void vbox_driver_unload(struct drm_device *dev);
+#else
+int vbox_driver_unload(struct drm_device *dev);
+#endif
+void vbox_driver_lastclose(struct drm_device *dev);
+
+struct vbox_gem_object;
+
+#ifndef VGA_PORT_HGSMI_HOST
+#define VGA_PORT_HGSMI_HOST 0x3b0
+#define VGA_PORT_HGSMI_GUEST 0x3d0
+#endif
+
+struct vbox_connector {
+ struct drm_connector base;
+ char name[32];
+ struct vbox_crtc *vbox_crtc;
+ struct {
+ u32 width;
+ u32 height;
+ bool disconnected;
+ } mode_hint;
+};
+
+struct vbox_crtc {
+ struct drm_crtc base;
+ bool blanked;
+ bool disconnected;
+ unsigned int crtc_id;
+ u32 fb_offset;
+ bool cursor_enabled;
+ u32 x_hint;
+ u32 y_hint;
+};
+
+struct vbox_encoder {
+ struct drm_encoder base;
+};
+
+struct vbox_framebuffer {
+ struct drm_framebuffer base;
+ struct drm_gem_object *obj;
+};
+
+struct vbox_fbdev {
+ struct drm_fb_helper helper;
+ struct vbox_framebuffer afb;
+ int size;
+ struct ttm_bo_kmap_obj mapping;
+ int x1, y1, x2, y2; /* dirty rect */
+ spinlock_t dirty_lock;
+};
+
+#define to_vbox_crtc(x) container_of(x, struct vbox_crtc, base)
+#define to_vbox_connector(x) container_of(x, struct vbox_connector, base)
+#define to_vbox_encoder(x) container_of(x, struct vbox_encoder, base)
+#define to_vbox_framebuffer(x) container_of(x, struct vbox_framebuffer, base)
+
+int vbox_mode_init(struct drm_device *dev);
+void vbox_mode_fini(struct drm_device *dev);
+
+#if RTLNX_VER_MAX(3,3,0)
+# define DRM_MODE_FB_CMD drm_mode_fb_cmd
+#else
+# define DRM_MODE_FB_CMD drm_mode_fb_cmd2
+#endif
+
+#if RTLNX_VER_MAX(3,15,0) && !RTLNX_RHEL_MAJ_PREREQ(7,1)
+# define CRTC_FB(crtc) ((crtc)->fb)
+#else
+# define CRTC_FB(crtc) ((crtc)->primary->fb)
+#endif
+
+void vbox_enable_accel(struct vbox_private *vbox);
+void vbox_disable_accel(struct vbox_private *vbox);
+void vbox_report_caps(struct vbox_private *vbox);
+
+void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb,
+ struct drm_clip_rect *rects,
+ unsigned int num_rects);
+
+int vbox_framebuffer_init(struct drm_device *dev,
+ struct vbox_framebuffer *vbox_fb,
+#if RTLNX_VER_MIN(4,5,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
+ const struct DRM_MODE_FB_CMD *mode_cmd,
+#else
+ struct DRM_MODE_FB_CMD *mode_cmd,
+#endif
+ struct drm_gem_object *obj);
+
+int vbox_fbdev_init(struct drm_device *dev);
+void vbox_fbdev_fini(struct drm_device *dev);
+void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr);
+
+struct vbox_bo {
+ struct ttm_buffer_object bo;
+ struct ttm_placement placement;
+ struct ttm_bo_kmap_obj kmap;
+ struct drm_gem_object gem;
+#if RTLNX_VER_MAX(3,18,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
+ u32 placements[3];
+#else
+ struct ttm_place placements[3];
+#endif
+ int pin_count;
+};
+
+#define gem_to_vbox_bo(gobj) container_of((gobj), struct vbox_bo, gem)
+
+static inline struct vbox_bo *vbox_bo(struct ttm_buffer_object *bo)
+{
+ return container_of(bo, struct vbox_bo, bo);
+}
+
+#define to_vbox_obj(x) container_of(x, struct vbox_gem_object, base)
+
+int vbox_dumb_create(struct drm_file *file,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+#if RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
+int vbox_dumb_destroy(struct drm_file *file,
+ struct drm_device *dev, u32 handle);
+#endif
+
+void vbox_gem_free_object(struct drm_gem_object *obj);
+int vbox_dumb_mmap_offset(struct drm_file *file,
+ struct drm_device *dev,
+ u32 handle, u64 *offset);
+
+#define DRM_FILE_PAGE_OFFSET (0x10000000ULL >> PAGE_SHIFT)
+
+int vbox_mm_init(struct vbox_private *vbox);
+void vbox_mm_fini(struct vbox_private *vbox);
+
+int vbox_bo_create(struct drm_device *dev, int size, int align,
+ u32 flags, struct vbox_bo **pvboxbo);
+
+int vbox_gem_create(struct drm_device *dev,
+ u32 size, bool iskernel, struct drm_gem_object **obj);
+
+#define VBOX_MEM_TYPE_VRAM 0x1
+#define VBOX_MEM_TYPE_SYSTEM 0x2
+
+int vbox_bo_pin(struct vbox_bo *bo, u32 mem_type, u64 *gpu_addr);
+int vbox_bo_unpin(struct vbox_bo *bo);
+
+static inline int vbox_bo_reserve(struct vbox_bo *bo, bool no_wait)
+{
+ int ret;
+
+#if RTLNX_VER_MIN(4,7,0) || RTLNX_RHEL_MAJ_PREREQ(7,4)
+ ret = ttm_bo_reserve(&bo->bo, true, no_wait, NULL);
+#else
+ ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+#endif
+ if (ret) {
+ if (ret != -ERESTARTSYS && ret != -EBUSY)
+ DRM_ERROR("reserve failed %p\n", bo);
+ return ret;
+ }
+ return 0;
+}
+
+static inline void vbox_bo_unreserve(struct vbox_bo *bo)
+{
+ ttm_bo_unreserve(&bo->bo);
+}
+
+void vbox_ttm_placement(struct vbox_bo *bo, u32 mem_type);
+int vbox_bo_push_sysram(struct vbox_bo *bo);
+int vbox_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/* vbox_prime.c */
+int vbox_gem_prime_pin(struct drm_gem_object *obj);
+void vbox_gem_prime_unpin(struct drm_gem_object *obj);
+struct sg_table *vbox_gem_prime_get_sg_table(struct drm_gem_object *obj);
+#if RTLNX_VER_MAX(3,18,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
+struct drm_gem_object *vbox_gem_prime_import_sg_table(
+ struct drm_device *dev, size_t size, struct sg_table *table);
+#else
+struct drm_gem_object *vbox_gem_prime_import_sg_table(
+ struct drm_device *dev, struct dma_buf_attachment *attach,
+ struct sg_table *table);
+#endif
+void *vbox_gem_prime_vmap(struct drm_gem_object *obj);
+void vbox_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+#if RTLNX_VER_MAX(6,6,0) && !RTLNX_RHEL_RANGE(9,4, 9,99)
+int vbox_gem_prime_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *area);
+#endif
+
+/* vbox_irq.c */
+int vbox_irq_init(struct vbox_private *vbox);
+void vbox_irq_fini(struct vbox_private *vbox);
+void vbox_report_hotplug(struct vbox_private *vbox);
+#if RTLNX_VER_MAX(5,15,0) && !RTLNX_RHEL_MAJ_PREREQ(9,1) && !RTLNX_SUSE_MAJ_PREREQ(15,5)
+irqreturn_t vbox_irq_handler(int irq, void *arg);
+#endif
+
+/* vbox_hgsmi.c */
+void *hgsmi_buffer_alloc(struct gen_pool *guest_pool, size_t size,
+ u8 channel, u16 channel_info);
+void hgsmi_buffer_free(struct gen_pool *guest_pool, void *buf);
+int hgsmi_buffer_submit(struct gen_pool *guest_pool, void *buf);
+
+static inline void vbox_write_ioport(u16 index, u16 data)
+{
+ outw(index, VBE_DISPI_IOPORT_INDEX);
+ outw(data, VBE_DISPI_IOPORT_DATA);
+}
+
+#endif /* !GA_INCLUDED_SRC_linux_drm_vbox_drv_h */
diff --git a/src/VBox/Additions/linux/drm/vbox_fb.c b/src/VBox/Additions/linux/drm/vbox_fb.c
new file mode 100644
index 00000000..37cf7ef1
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_fb.c
@@ -0,0 +1,541 @@
+/* $Id: vbox_fb.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ * This file is based on ast_fb.c
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com,
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include "vbox_drv.h"
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <VBoxVideo.h>
+
+#if RTLNX_VER_MIN(6,2,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+# define VBOX_FBDEV_INFO(_helper) _helper.info
+#else
+# define VBOX_FBDEV_INFO(_helper) _helper.fbdev
+#endif
+
+#if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
+/**
+ * Tell the host about dirty rectangles to update.
+ */
+static void vbox_dirty_update(struct vbox_fbdev *fbdev,
+ int x, int y, int width, int height)
+{
+ struct drm_gem_object *obj;
+ struct vbox_bo *bo;
+ int ret = -EBUSY;
+ bool store_for_later = false;
+ int x2, y2;
+ unsigned long flags;
+ struct drm_clip_rect rect;
+
+ obj = fbdev->afb.obj;
+ bo = gem_to_vbox_bo(obj);
+
+ /*
+ * try and reserve the BO, if we fail with busy
+ * then the BO is being moved and we should
+ * store up the damage until later.
+ */
+ if (drm_can_sleep())
+ ret = vbox_bo_reserve(bo, true);
+ if (ret) {
+ if (ret != -EBUSY)
+ return;
+
+ store_for_later = true;
+ }
+
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+ spin_lock_irqsave(&fbdev->dirty_lock, flags);
+
+ if (fbdev->y1 < y)
+ y = fbdev->y1;
+ if (fbdev->y2 > y2)
+ y2 = fbdev->y2;
+ if (fbdev->x1 < x)
+ x = fbdev->x1;
+ if (fbdev->x2 > x2)
+ x2 = fbdev->x2;
+
+ if (store_for_later) {
+ fbdev->x1 = x;
+ fbdev->x2 = x2;
+ fbdev->y1 = y;
+ fbdev->y2 = y2;
+ spin_unlock_irqrestore(&fbdev->dirty_lock, flags);
+ return;
+ }
+
+ fbdev->x1 = INT_MAX;
+ fbdev->y1 = INT_MAX;
+ fbdev->x2 = 0;
+ fbdev->y2 = 0;
+
+ spin_unlock_irqrestore(&fbdev->dirty_lock, flags);
+
+ /*
+ * Not sure why the original code subtracted 1 here, but I will keep
+ * it that way to avoid unnecessary differences.
+ */
+ rect.x1 = x;
+ rect.x2 = x2 + 1;
+ rect.y1 = y;
+ rect.y2 = y2 + 1;
+ vbox_framebuffer_dirty_rectangles(&fbdev->afb.base, &rect, 1);
+
+ vbox_bo_unreserve(bo);
+}
+#endif /* RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4) */
+
+#ifdef CONFIG_FB_DEFERRED_IO
+# if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
+static void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+ struct vbox_fbdev *fbdev = info->par;
+ unsigned long start, end, min, max;
+ struct page *page;
+ int y1, y2;
+
+ min = ULONG_MAX;
+ max = 0;
+ list_for_each_entry(page, pagelist, lru) {
+ start = page->index << PAGE_SHIFT;
+ end = start + PAGE_SIZE - 1;
+ min = min(min, start);
+ max = max(max, end);
+ }
+
+ if (min < max) {
+ y1 = min / info->fix.line_length;
+ y2 = (max / info->fix.line_length) + 1;
+ DRM_INFO("%s: Calling dirty update: 0, %d, %d, %d\n",
+ __func__, y1, info->var.xres, y2 - y1 - 1);
+ vbox_dirty_update(fbdev, 0, y1, info->var.xres, y2 - y1 - 1);
+ }
+}
+# endif
+
+static struct fb_deferred_io vbox_defio = {
+ .delay = HZ / 30,
+ .deferred_io = drm_fb_helper_deferred_io,
+};
+#endif /* CONFIG_FB_DEFERRED_IO */
+
+#if RTLNX_VER_MAX(4,3,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
+static void drm_fb_helper_sys_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct vbox_fbdev *fbdev = info->par;
+
+ sys_fillrect(info, rect);
+ vbox_dirty_update(fbdev, rect->dx, rect->dy, rect->width, rect->height);
+}
+
+static void drm_fb_helper_sys_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct vbox_fbdev *fbdev = info->par;
+
+ sys_copyarea(info, area);
+ vbox_dirty_update(fbdev, area->dx, area->dy, area->width, area->height);
+}
+
+static void drm_fb_helper_sys_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct vbox_fbdev *fbdev = info->par;
+
+ sys_imageblit(info, image);
+ vbox_dirty_update(fbdev, image->dx, image->dy, image->width,
+ image->height);
+}
+#endif /* RTLNX_VER_MAX(4,3,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3) */
+
+static struct fb_ops vboxfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+#if RTLNX_VER_MIN(6,5,0) || RTLNX_RHEL_RANGE(9,4, 9,99)
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+ .fb_mmap = NULL,
+#else
+ .fb_fillrect = drm_fb_helper_sys_fillrect,
+ .fb_copyarea = drm_fb_helper_sys_copyarea,
+ .fb_imageblit = drm_fb_helper_sys_imageblit,
+#endif
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_setcmap = drm_fb_helper_setcmap,
+ .fb_debug_enter = drm_fb_helper_debug_enter,
+ .fb_debug_leave = drm_fb_helper_debug_leave,
+};
+
+static int vboxfb_create_object(struct vbox_fbdev *fbdev,
+ struct DRM_MODE_FB_CMD *mode_cmd,
+ struct drm_gem_object **gobj_p)
+{
+ struct drm_device *dev = fbdev->helper.dev;
+ u32 size;
+ struct drm_gem_object *gobj;
+#if RTLNX_VER_MAX(3,3,0)
+ u32 pitch = mode_cmd->pitch;
+#else
+ u32 pitch = mode_cmd->pitches[0];
+#endif
+
+ int ret;
+
+ size = pitch * mode_cmd->height;
+ ret = vbox_gem_create(dev, size, true, &gobj);
+ if (ret)
+ return ret;
+
+ *gobj_p = gobj;
+
+ return 0;
+}
+
+#if RTLNX_VER_MAX(4,3,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
+static struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *helper)
+{
+ struct fb_info *info;
+ struct vbox_fbdev *fbdev =
+ container_of(helper, struct vbox_fbdev, helper);
+ struct drm_device *dev = fbdev->helper.dev;
+ struct device *device = &dev->pdev->dev;
+
+ info = framebuffer_alloc(0, device);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+ fbdev->helper.fbdev = info;
+
+ if (fb_alloc_cmap(&info->cmap, 256, 0))
+ return ERR_PTR(-ENOMEM);
+
+ info->apertures = alloc_apertures(1);
+ if (!info->apertures)
+ return ERR_PTR(-ENOMEM);
+
+ return info;
+}
+#endif
+
+static int vboxfb_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct vbox_fbdev *fbdev =
+ container_of(helper, struct vbox_fbdev, helper);
+ struct drm_device *dev = fbdev->helper.dev;
+ struct DRM_MODE_FB_CMD mode_cmd;
+ struct drm_framebuffer *fb;
+ struct fb_info *info;
+ struct drm_gem_object *gobj;
+ struct vbox_bo *bo;
+ int size, ret;
+ u32 pitch;
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ pitch = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
+#if RTLNX_VER_MAX(3,3,0)
+ mode_cmd.bpp = sizes->surface_bpp;
+ mode_cmd.depth = sizes->surface_depth;
+ mode_cmd.pitch = pitch;
+#else
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+ mode_cmd.pitches[0] = pitch;
+#endif
+
+ size = pitch * mode_cmd.height;
+
+ ret = vboxfb_create_object(fbdev, &mode_cmd, &gobj);
+ if (ret) {
+ DRM_ERROR("failed to create fbcon backing object %d\n", ret);
+ return ret;
+ }
+
+ ret = vbox_framebuffer_init(dev, &fbdev->afb, &mode_cmd, gobj);
+ if (ret)
+ return ret;
+
+ bo = gem_to_vbox_bo(gobj);
+
+ ret = vbox_bo_reserve(bo, false);
+ if (ret)
+ return ret;
+
+ ret = vbox_bo_pin(bo, VBOX_MEM_TYPE_VRAM, NULL);
+ if (ret) {
+ vbox_bo_unreserve(bo);
+ return ret;
+ }
+
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ ret = ttm_bo_kmap(&bo->bo, 0, VBOX_BO_RESOURCE_NUM_PAGES(bo->bo.resource), &bo->kmap);
+#elif RTLNX_VER_MIN(5,12,0) || RTLNX_RHEL_MAJ_PREREQ(8,5)
+ ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.mem.num_pages, &bo->kmap);
+#else
+ ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+#endif
+
+ vbox_bo_unreserve(bo);
+ if (ret) {
+ DRM_ERROR("failed to kmap fbcon\n");
+ return ret;
+ }
+
+#if RTLNX_VER_MIN(6,2,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+ info = drm_fb_helper_alloc_info(helper);
+#else
+ info = drm_fb_helper_alloc_fbi(helper);
+#endif
+ if (IS_ERR(info))
+ return -PTR_ERR(info);
+
+ info->par = fbdev;
+
+ fbdev->size = size;
+
+ fb = &fbdev->afb.base;
+ fbdev->helper.fb = fb;
+
+ strcpy(info->fix.id, "vboxdrmfb");
+
+ /*
+ * The last flag forces a mode set on VT switches even if the kernel
+ * does not think it is needed.
+ */
+#if RTLNX_VER_MIN(6,6,0)
+ info->flags = FBINFO_MISC_ALWAYS_SETPAR;
+#else
+ info->flags = FBINFO_DEFAULT | FBINFO_MISC_ALWAYS_SETPAR;
+#endif
+ info->fbops = &vboxfb_ops;
+
+#if RTLNX_VER_MAX(6,3,0) && !RTLNX_RHEL_RANGE(8,9, 8,99) && !RTLNX_RHEL_RANGE(9,3, 9,99)
+ /*
+ * This seems to be done for safety checking that the framebuffer
+ * is not registered twice by different drivers.
+ */
+ info->apertures->ranges[0].base = pci_resource_start(VBOX_DRM_TO_PCI_DEV(dev), 0);
+ info->apertures->ranges[0].size = pci_resource_len(VBOX_DRM_TO_PCI_DEV(dev), 0);
+#endif
+
+#if RTLNX_VER_MIN(5,2,0) || RTLNX_RHEL_MAJ_PREREQ(8,2)
+ /*
+ * The corresponding 5.2-rc1 Linux DRM kernel changes have been
+ * also backported to older RedHat based 4.18.0 Linux kernels.
+ */
+ drm_fb_helper_fill_info(info, &fbdev->helper, sizes);
+#elif RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7, 5)
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
+#else
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+#endif
+#if RTLNX_VER_MAX(5,2,0) && !RTLNX_RHEL_MAJ_PREREQ(8,2)
+ drm_fb_helper_fill_var(info, &fbdev->helper, sizes->fb_width,
+ sizes->fb_height);
+#endif
+
+#if RTLNX_VER_MIN(6,5,0)
+ info->screen_buffer = (char *)bo->kmap.virtual;
+ info->fix.smem_start = page_to_phys(vmalloc_to_page(bo->kmap.virtual));
+#endif
+ info->screen_base = (char __iomem *)bo->kmap.virtual;
+ info->screen_size = size;
+
+#ifdef CONFIG_FB_DEFERRED_IO
+# if RTLNX_VER_MIN(5,19,0) || RTLNX_RHEL_RANGE(8,8, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99) || RTLNX_SUSE_MAJ_PREREQ(15,5)
+ info->fix.smem_len = info->screen_size;
+# endif
+ info->fbdefio = &vbox_defio;
+# if RTLNX_VER_MIN(5,19,0)
+ ret = fb_deferred_io_init(info);
+ if (ret)
+ {
+ DRM_ERROR("failed to initialize deferred io: %d\n", ret);
+ return ret;
+ }
+# endif
+ fb_deferred_io_init(info);
+#endif
+
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
+
+ DRM_DEBUG_KMS("allocated %dx%d\n", fb->width, fb->height);
+
+ return 0;
+}
+
+static struct drm_fb_helper_funcs vbox_fb_helper_funcs = {
+ .fb_probe = vboxfb_create,
+};
+
+#if RTLNX_VER_MAX(4,3,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
+static void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
+{
+ if (fb_helper && fb_helper->fbdev)
+ unregister_framebuffer(fb_helper->fbdev);
+}
+#endif
+
+void vbox_fbdev_fini(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+ struct vbox_fbdev *fbdev = vbox->fbdev;
+ struct vbox_framebuffer *afb = &fbdev->afb;
+
+#ifdef CONFIG_FB_DEFERRED_IO
+ if (VBOX_FBDEV_INFO(fbdev->helper) && VBOX_FBDEV_INFO(fbdev->helper)->fbdefio)
+ fb_deferred_io_cleanup(VBOX_FBDEV_INFO(fbdev->helper));
+#endif
+
+#if RTLNX_VER_MIN(6,2,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+ drm_fb_helper_unregister_info(&fbdev->helper);
+#else
+ drm_fb_helper_unregister_fbi(&fbdev->helper);
+#endif
+
+ if (afb->obj) {
+ struct vbox_bo *bo = gem_to_vbox_bo(afb->obj);
+
+ if (!vbox_bo_reserve(bo, false)) {
+ if (bo->kmap.virtual)
+ ttm_bo_kunmap(&bo->kmap);
+ /*
+ * QXL does this, but is it really needed before
+ * freeing?
+ */
+ if (bo->pin_count)
+ vbox_bo_unpin(bo);
+ vbox_bo_unreserve(bo);
+ }
+#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ drm_gem_object_put(afb->obj);
+#else
+ drm_gem_object_put_unlocked(afb->obj);
+#endif
+ afb->obj = NULL;
+ }
+ drm_fb_helper_fini(&fbdev->helper);
+
+#if RTLNX_VER_MIN(3,9,0)
+ drm_framebuffer_unregister_private(&afb->base);
+#endif
+ drm_framebuffer_cleanup(&afb->base);
+}
+
+int vbox_fbdev_init(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+ struct vbox_fbdev *fbdev;
+ int ret = 0;
+
+ fbdev = devm_kzalloc(dev->dev, sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev)
+ return -ENOMEM;
+
+ vbox->fbdev = fbdev;
+ spin_lock_init(&fbdev->dirty_lock);
+
+#if RTLNX_VER_MIN(6,3,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+ drm_fb_helper_prepare(dev, &fbdev->helper, 32, &vbox_fb_helper_funcs);
+#elif RTLNX_VER_MIN(3,17,0) || RTLNX_RHEL_MIN(7,2)
+ drm_fb_helper_prepare(dev, &fbdev->helper, &vbox_fb_helper_funcs);
+#else
+ fbdev->helper.funcs = &vbox_fb_helper_funcs;
+#endif
+
+#if RTLNX_VER_MIN(5,7,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ ret = drm_fb_helper_init(dev, &fbdev->helper);
+#elif RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+ ret = drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs);
+#else /* < 4.11.0 */
+ ret =
+ drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs,
+ vbox->num_crtcs);
+#endif
+ if (ret)
+ return ret;
+
+#if RTLNX_VER_MAX(5,7,0) && !RTLNX_RHEL_MAJ_PREREQ(8,4) && !RTLNX_SUSE_MAJ_PREREQ(15,3)
+ ret = drm_fb_helper_single_add_all_connectors(&fbdev->helper);
+ if (ret)
+ goto err_fini;
+#endif
+
+ /* disable all the possible outputs/crtcs before entering KMS mode */
+ drm_helper_disable_unused_functions(dev);
+
+#if RTLNX_VER_MIN(6,3,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+ ret = drm_fb_helper_initial_config(&fbdev->helper);
+#else
+ ret = drm_fb_helper_initial_config(&fbdev->helper, 32);
+#endif
+ if (ret)
+ goto err_fini;
+
+ return 0;
+
+err_fini:
+ drm_fb_helper_fini(&fbdev->helper);
+ return ret;
+}
+
+void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr)
+{
+ struct fb_info *fbdev = VBOX_FBDEV_INFO(vbox->fbdev->helper);
+
+#if RTLNX_VER_MIN(6,3,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+ fbdev->fix.smem_start = pci_resource_start(VBOX_DRM_TO_PCI_DEV(vbox->fbdev->helper.dev), 0) + gpu_addr;
+#else
+ fbdev->fix.smem_start = fbdev->apertures->ranges[0].base + gpu_addr;
+#endif
+ fbdev->fix.smem_len = vbox->available_vram_size - gpu_addr;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_hgsmi.c b/src/VBox/Additions/linux/drm/vbox_hgsmi.c
new file mode 100644
index 00000000..de47f89d
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_hgsmi.c
@@ -0,0 +1,130 @@
+/** @file
+ * VirtualBox Additions Linux kernel video driver hgsmi interface code
+ */
+
+/*
+ * Contributed by Hans de Goede <hdegoede@redhat.com>
+ *
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <HGSMIBase.h>
+#include <VBoxVideoVBE.h>
+
+/* One-at-a-Time Hash from http://www.burtleburtle.net/bob/hash/doobs.html */
+static u32 hgsmi_hash_process(u32 hash, const u8 *data, int size)
+{
+ while (size--) {
+ hash += *data++;
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+
+ return hash;
+}
+
+static u32 hgsmi_hash_end(u32 hash)
+{
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash;
+}
+
+/* Not really a checksum but that is the naming used in all vbox code */
+static u32 hgsmi_checksum(u32 offset,
+ const HGSMIBUFFERHEADER *header,
+ const HGSMIBUFFERTAIL *tail)
+{
+ u32 checksum;
+
+ checksum = hgsmi_hash_process(0, (u8 *)&offset, sizeof(offset));
+ checksum = hgsmi_hash_process(checksum, (u8 *)header, sizeof(*header));
+ /* 4 -> Do not checksum the checksum itself */
+ checksum = hgsmi_hash_process(checksum, (u8 *)tail, 4);
+
+ return hgsmi_hash_end(checksum);
+}
+
+#if RTLNX_VER_MAX(3,13,0)
+void *gen_pool_dma_alloc(struct gen_pool *pool, size_t size, dma_addr_t *dma)
+{
+ unsigned long vaddr = gen_pool_alloc(pool, size);
+
+ if (vaddr)
+ *dma = gen_pool_virt_to_phys(pool, vaddr);
+ return (void *)vaddr;
+}
+#endif
+
+void *hgsmi_buffer_alloc(struct gen_pool *guest_pool, size_t size,
+ u8 channel, u16 channel_info)
+{
+ HGSMIBUFFERHEADER *h;
+ HGSMIBUFFERTAIL *t;
+ size_t total_size;
+ dma_addr_t offset;
+
+ total_size = size + sizeof(*h) + sizeof(*t);
+ h = gen_pool_dma_alloc(guest_pool, total_size, &offset);
+ if (!h)
+ return NULL;
+
+ t = (HGSMIBUFFERTAIL *)((u8 *)h + sizeof(*h) + size);
+
+ h->u8Flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE;
+ h->u32DataSize = size;
+ h->u8Channel = channel;
+ h->u16ChannelInfo = channel_info;
+ memset(&h->u.au8Union, 0, sizeof(h->u.au8Union));
+
+ t->u32Reserved = 0;
+ t->u32Checksum = hgsmi_checksum(offset, h, t);
+
+ return (u8 *)h + sizeof(*h);
+}
+
+void hgsmi_buffer_free(struct gen_pool *guest_pool, void *buf)
+{
+ HGSMIBUFFERHEADER *h =
+ (HGSMIBUFFERHEADER *)((u8 *)buf - sizeof(*h));
+ size_t total_size = h->u32DataSize + sizeof(*h) +
+ sizeof(HGSMIBUFFERTAIL);
+
+ gen_pool_free(guest_pool, (unsigned long)h, total_size);
+}
+
+int hgsmi_buffer_submit(struct gen_pool *guest_pool, void *buf)
+{
+ phys_addr_t offset;
+
+ offset = gen_pool_virt_to_phys(guest_pool, (unsigned long)buf -
+ sizeof(HGSMIBUFFERHEADER));
+ outl(offset, VGA_PORT_HGSMI_GUEST);
+ /* Make the compiler aware that the host has changed memory. */
+ mb();
+
+ return 0;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_irq.c b/src/VBox/Additions/linux/drm/vbox_irq.c
new file mode 100644
index 00000000..594388f8
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_irq.c
@@ -0,0 +1,225 @@
+/* $Id: vbox_irq.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ * This file is based on qxl_irq.c
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Dave Airlie
+ * Alon Levy
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+#include "vbox_drv.h"
+
+#if RTLNX_VER_MAX(5,1,0)
+# include <drm/drm_crtc_helper.h>
+# if RTLNX_RHEL_MAJ_PREREQ(8,1)
+# include <drm/drm_probe_helper.h>
+# endif
+#else
+# include <drm/drm_probe_helper.h>
+#endif
+#include <VBoxVideo.h>
+
+static void vbox_clear_irq(void)
+{
+ outl((u32)~0, VGA_PORT_HGSMI_HOST);
+}
+
+static u32 vbox_get_flags(struct vbox_private *vbox)
+{
+ return readl(vbox->guest_heap + HOST_FLAGS_OFFSET);
+}
+
+void vbox_report_hotplug(struct vbox_private *vbox)
+{
+ schedule_work(&vbox->hotplug_work);
+}
+
+irqreturn_t vbox_irq_handler(int irq, void *arg)
+{
+ struct drm_device *dev = (struct drm_device *)arg;
+ struct vbox_private *vbox = (struct vbox_private *)dev->dev_private;
+ u32 host_flags = vbox_get_flags(vbox);
+
+ if (!(host_flags & HGSMIHOSTFLAGS_IRQ))
+ return IRQ_NONE;
+
+ /*
+ * Due to a bug in the initial host implementation of hot-plug irqs,
+ * the hot-plug and cursor capability flags were never cleared.
+ * Fortunately we can tell when they would have been set by checking
+ * that the VSYNC flag is not set.
+ */
+ if (host_flags &
+ (HGSMIHOSTFLAGS_HOTPLUG | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES) &&
+ !(host_flags & HGSMIHOSTFLAGS_VSYNC))
+ vbox_report_hotplug(vbox);
+
+ vbox_clear_irq();
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * Check that the position hints provided by the host are suitable for GNOME
+ * shell (i.e. all screens disjoint and hints for all enabled screens) and if
+ * not replace them with default ones. Providing valid hints improves the
+ * chances that we will get a known screen layout for pointer mapping.
+ */
+static void validate_or_set_position_hints(struct vbox_private *vbox)
+{
+ struct VBVAMODEHINT *hintsi, *hintsj;
+ bool valid = true;
+ u16 currentx = 0;
+ int i, j;
+
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ for (j = 0; j < i; ++j) {
+ hintsi = &vbox->last_mode_hints[i];
+ hintsj = &vbox->last_mode_hints[j];
+
+ if (hintsi->fEnabled && hintsj->fEnabled) {
+ if (hintsi->dx >= 0xffff ||
+ hintsi->dy >= 0xffff ||
+ hintsj->dx >= 0xffff ||
+ hintsj->dy >= 0xffff ||
+ (hintsi->dx <
+ hintsj->dx + (hintsj->cx & 0x8fff) &&
+ hintsi->dx + (hintsi->cx & 0x8fff) >
+ hintsj->dx) ||
+ (hintsi->dy <
+ hintsj->dy + (hintsj->cy & 0x8fff) &&
+ hintsi->dy + (hintsi->cy & 0x8fff) >
+ hintsj->dy))
+ valid = false;
+ }
+ }
+ }
+ if (!valid)
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ if (vbox->last_mode_hints[i].fEnabled) {
+ vbox->last_mode_hints[i].dx = currentx;
+ vbox->last_mode_hints[i].dy = 0;
+ currentx +=
+ vbox->last_mode_hints[i].cx & 0x8fff;
+ }
+ }
+}
+
+/**
+ * Query the host for the most recent video mode hints.
+ */
+static void vbox_update_mode_hints(struct vbox_private *vbox)
+{
+ struct drm_device *dev = vbox->dev;
+ struct drm_connector *connector;
+ struct vbox_connector *vbox_conn;
+ struct VBVAMODEHINT *hints;
+ u16 flags;
+ bool disconnected;
+ unsigned int crtc_id;
+ int ret;
+
+ ret = VBoxHGSMIGetModeHints(vbox->guest_pool, vbox->num_crtcs,
+ vbox->last_mode_hints);
+ if (ret) {
+ DRM_ERROR("vboxvideo: hgsmi_get_mode_hints failed: %d\n", ret);
+ return;
+ }
+
+ validate_or_set_position_hints(vbox);
+#if RTLNX_VER_MIN(3,9,0)
+ drm_modeset_lock_all(dev);
+#else
+ mutex_lock(&dev->mode_config.mutex);
+#endif
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ vbox_conn = to_vbox_connector(connector);
+
+ hints = &vbox->last_mode_hints[vbox_conn->vbox_crtc->crtc_id];
+ if (hints->magic != VBVAMODEHINT_MAGIC)
+ continue;
+
+ disconnected = !(hints->fEnabled);
+ crtc_id = vbox_conn->vbox_crtc->crtc_id;
+ vbox_conn->mode_hint.width = hints->cx;
+ vbox_conn->mode_hint.height = hints->cy;
+ vbox_conn->vbox_crtc->x_hint = hints->dx;
+ vbox_conn->vbox_crtc->y_hint = hints->dy;
+ vbox_conn->mode_hint.disconnected = disconnected;
+
+ if (vbox_conn->vbox_crtc->disconnected == disconnected)
+ continue;
+
+ if (disconnected)
+ flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED;
+ else
+ flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_BLANK;
+
+ VBoxHGSMIProcessDisplayInfo(vbox->guest_pool, crtc_id, 0, 0, 0,
+ hints->cx * 4, hints->cx,
+ hints->cy, 0, flags);
+
+ vbox_conn->vbox_crtc->disconnected = disconnected;
+ }
+#if RTLNX_VER_MIN(3,9,0)
+ drm_modeset_unlock_all(dev);
+#else
+ mutex_unlock(&dev->mode_config.mutex);
+#endif
+}
+
+static void vbox_hotplug_worker(struct work_struct *work)
+{
+ struct vbox_private *vbox = container_of(work, struct vbox_private,
+ hotplug_work);
+
+ vbox_update_mode_hints(vbox);
+ drm_kms_helper_hotplug_event(vbox->dev);
+}
+
+int vbox_irq_init(struct vbox_private *vbox)
+{
+ INIT_WORK(&vbox->hotplug_work, vbox_hotplug_worker);
+ vbox_update_mode_hints(vbox);
+#if RTLNX_VER_MIN(5,15,0) || RTLNX_RHEL_RANGE(8,7, 8,99) || RTLNX_RHEL_MAJ_PREREQ(9,1) || RTLNX_SUSE_MAJ_PREREQ(15,5)
+ return request_irq(VBOX_DRM_TO_PCI_DEV(vbox->dev)->irq, vbox_irq_handler, IRQF_SHARED, vbox->dev->driver->name, vbox->dev);
+#elif RTLNX_VER_MIN(3,16,0) || RTLNX_RHEL_MAJ_PREREQ(7,1)
+ return drm_irq_install(vbox->dev, VBOX_DRM_TO_PCI_DEV(vbox->dev)->irq);
+#else
+ return drm_irq_install(vbox->dev);
+#endif
+}
+
+void vbox_irq_fini(struct vbox_private *vbox)
+{
+#if RTLNX_VER_MIN(5,15,0) || RTLNX_RHEL_RANGE(8,7, 8,99) || RTLNX_RHEL_MAJ_PREREQ(9,1) || RTLNX_SUSE_MAJ_PREREQ(15,5)
+ free_irq(VBOX_DRM_TO_PCI_DEV(vbox->dev)->irq, vbox->dev);
+#else
+ drm_irq_uninstall(vbox->dev);
+#endif
+ flush_work(&vbox->hotplug_work);
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_main.c b/src/VBox/Additions/linux/drm/vbox_main.c
new file mode 100644
index 00000000..ba6ee3c1
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_main.c
@@ -0,0 +1,704 @@
+/* $Id: vbox_main.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ * This file is based on ast_main.c
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>,
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+#include "vbox_drv.h"
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#if RTLNX_VER_MIN(6,3,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+# include <drm/drm_modeset_helper.h>
+#endif
+
+#include <VBoxVideoGuest.h>
+#include <VBoxVideoVBE.h>
+
+#include "hgsmi_channels.h"
+
+static void vbox_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct vbox_framebuffer *vbox_fb = to_vbox_framebuffer(fb);
+
+ if (vbox_fb->obj)
+#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ drm_gem_object_put(vbox_fb->obj);
+#else
+ drm_gem_object_put_unlocked(vbox_fb->obj);
+#endif
+
+ drm_framebuffer_cleanup(fb);
+ kfree(fb);
+}
+
+void vbox_enable_accel(struct vbox_private *vbox)
+{
+ unsigned int i;
+ struct VBVABUFFER *vbva;
+
+ if (!vbox->vbva_info || !vbox->vbva_buffers) {
+ /* Should never happen... */
+ DRM_ERROR("vboxvideo: failed to set up VBVA.\n");
+ return;
+ }
+
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ if (vbox->vbva_info[i].pVBVA)
+ continue;
+
+ vbva = (void __force *)vbox->vbva_buffers +
+ i * VBVA_MIN_BUFFER_SIZE;
+ if (!VBoxVBVAEnable(&vbox->vbva_info[i],
+ vbox->guest_pool, vbva, i)) {
+ /* very old host or driver error. */
+ DRM_ERROR("vboxvideo: vbva_enable failed\n");
+ return;
+ }
+ }
+}
+
+void vbox_disable_accel(struct vbox_private *vbox)
+{
+ unsigned int i;
+
+ for (i = 0; i < vbox->num_crtcs; ++i)
+ VBoxVBVADisable(&vbox->vbva_info[i], vbox->guest_pool, i);
+}
+
+void vbox_report_caps(struct vbox_private *vbox)
+{
+ u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION |
+ VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY;
+
+ if (vbox->initial_mode_queried)
+ caps |= VBVACAPS_VIDEO_MODE_HINTS;
+
+ VBoxHGSMISendCapsInfo(vbox->guest_pool, caps);
+}
+
+/**
+ * Send information about dirty rectangles to VBVA. If necessary we enable
+ * VBVA first, as this is normally disabled after a change of master in case
+ * the new master does not send dirty rectangle information (is this even
+ * allowed?)
+ */
+void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb,
+ struct drm_clip_rect *rects,
+ unsigned int num_rects)
+{
+ struct vbox_private *vbox = fb->dev->dev_private;
+ struct drm_crtc *crtc;
+ unsigned int i;
+
+ /* The user can send rectangles, we do not need the timer. */
+ vbox->need_refresh_timer = false;
+ mutex_lock(&vbox->hw_mutex);
+ list_for_each_entry(crtc, &fb->dev->mode_config.crtc_list, head) {
+ if (CRTC_FB(crtc) != fb)
+ continue;
+
+ for (i = 0; i < num_rects; ++i) {
+ VBVACMDHDR cmd_hdr;
+ unsigned int crtc_id = to_vbox_crtc(crtc)->crtc_id;
+
+ if ((rects[i].x1 > crtc->x + crtc->hwmode.hdisplay) ||
+ (rects[i].y1 > crtc->y + crtc->hwmode.vdisplay) ||
+ (rects[i].x2 < crtc->x) ||
+ (rects[i].y2 < crtc->y))
+ continue;
+
+ cmd_hdr.x = (s16)rects[i].x1;
+ cmd_hdr.y = (s16)rects[i].y1;
+ cmd_hdr.w = (u16)rects[i].x2 - rects[i].x1;
+ cmd_hdr.h = (u16)rects[i].y2 - rects[i].y1;
+
+ if (!VBoxVBVABufferBeginUpdate(&vbox->vbva_info[crtc_id],
+ vbox->guest_pool))
+ continue;
+
+ VBoxVBVAWrite(&vbox->vbva_info[crtc_id], vbox->guest_pool,
+ &cmd_hdr, sizeof(cmd_hdr));
+ VBoxVBVABufferEndUpdate(&vbox->vbva_info[crtc_id]);
+ }
+ }
+ mutex_unlock(&vbox->hw_mutex);
+}
+
+static int vbox_user_framebuffer_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int flags, unsigned int color,
+ struct drm_clip_rect *rects,
+ unsigned int num_rects)
+{
+ vbox_framebuffer_dirty_rectangles(fb, rects, num_rects);
+
+ return 0;
+}
+
+static const struct drm_framebuffer_funcs vbox_fb_funcs = {
+ .destroy = vbox_user_framebuffer_destroy,
+ .dirty = vbox_user_framebuffer_dirty,
+};
+
+int vbox_framebuffer_init(struct drm_device *dev,
+ struct vbox_framebuffer *vbox_fb,
+#if RTLNX_VER_MIN(4,5,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
+ const struct DRM_MODE_FB_CMD *mode_cmd,
+#else
+ struct DRM_MODE_FB_CMD *mode_cmd,
+#endif
+ struct drm_gem_object *obj)
+{
+ int ret;
+
+#if RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+ drm_helper_mode_fill_fb_struct(dev, &vbox_fb->base, mode_cmd);
+#else
+ drm_helper_mode_fill_fb_struct(&vbox_fb->base, mode_cmd);
+#endif
+ vbox_fb->obj = obj;
+ ret = drm_framebuffer_init(dev, &vbox_fb->base, &vbox_fb_funcs);
+ if (ret) {
+ DRM_ERROR("framebuffer init failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct drm_framebuffer *vbox_user_framebuffer_create(
+ struct drm_device *dev,
+ struct drm_file *filp,
+#if RTLNX_VER_MIN(4,5,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+#else
+ struct drm_mode_fb_cmd2 *mode_cmd)
+#endif
+{
+ struct drm_gem_object *obj;
+ struct vbox_framebuffer *vbox_fb;
+ int ret = -ENOMEM;
+
+#if RTLNX_VER_MIN(4,7,0) || RTLNX_RHEL_MAJ_PREREQ(7,4)
+ obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
+#else
+ obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]);
+#endif
+ if (!obj)
+ return ERR_PTR(-ENOENT);
+
+ vbox_fb = kzalloc(sizeof(*vbox_fb), GFP_KERNEL);
+ if (!vbox_fb)
+ goto err_unref_obj;
+
+ ret = vbox_framebuffer_init(dev, vbox_fb, mode_cmd, obj);
+ if (ret)
+ goto err_free_vbox_fb;
+
+ return &vbox_fb->base;
+
+err_free_vbox_fb:
+ kfree(vbox_fb);
+err_unref_obj:
+#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ drm_gem_object_put(obj);
+#else
+ drm_gem_object_put_unlocked(obj);
+#endif
+ return ERR_PTR(ret);
+}
+
+static const struct drm_mode_config_funcs vbox_mode_funcs = {
+ .fb_create = vbox_user_framebuffer_create,
+};
+
+#if RTLNX_VER_MAX(4,0,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
+# define pci_iomap_range(dev, bar, offset, maxlen) \
+ ioremap(pci_resource_start(dev, bar) + (offset), maxlen)
+#endif
+
+/**
+ * Tell the host about the views. This design originally targeted the
+ * Windows XP driver architecture and assumed that each screen would
+ * have a dedicated frame buffer with the command buffer following it,
+ * the whole being a "view". The host works out which screen a command
+ * buffer belongs to by checking whether it is in the first view, then
+ * whether it is in the second and so on. The first match wins. We
+ * cheat around this by making the first view be the managed memory
+ * plus the first command buffer, the second the same plus the second
+ * buffer and so on.
+ */
+static int vbox_set_views(struct vbox_private *vbox)
+{
+ VBVAINFOVIEW *p;
+ int i;
+
+ p = VBoxHGSMIBufferAlloc(vbox->guest_pool, sizeof(*p),
+ HGSMI_CH_VBVA, VBVA_INFO_VIEW);
+ if (!p)
+ return -ENOMEM;
+
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ p->u32ViewIndex = i;
+ p->u32ViewOffset = 0;
+ p->u32ViewSize = vbox->available_vram_size +
+ i * VBVA_MIN_BUFFER_SIZE;
+ p->u32MaxScreenSize = vbox->available_vram_size;
+
+ VBoxHGSMIBufferSubmit(vbox->guest_pool, p);
+ }
+
+ VBoxHGSMIBufferFree(vbox->guest_pool, p);
+
+ return 0;
+}
+
+static int vbox_accel_init(struct vbox_private *vbox)
+{
+ unsigned int i, ret;
+
+ vbox->vbva_info = devm_kcalloc(vbox->dev->dev, vbox->num_crtcs,
+ sizeof(*vbox->vbva_info), GFP_KERNEL);
+ if (!vbox->vbva_info)
+ return -ENOMEM;
+
+ /* Take a command buffer for each screen from the end of usable VRAM. */
+ vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE;
+
+ vbox->vbva_buffers = pci_iomap_range(VBOX_DRM_TO_PCI_DEV(vbox->dev), 0,
+ vbox->available_vram_size,
+ vbox->num_crtcs *
+ VBVA_MIN_BUFFER_SIZE);
+ if (!vbox->vbva_buffers)
+ return -ENOMEM;
+
+ for (i = 0; i < vbox->num_crtcs; ++i)
+ VBoxVBVASetupBufferContext(&vbox->vbva_info[i],
+ vbox->available_vram_size +
+ i * VBVA_MIN_BUFFER_SIZE,
+ VBVA_MIN_BUFFER_SIZE);
+
+ vbox_enable_accel(vbox);
+ ret = vbox_set_views(vbox);
+ if (ret)
+ goto err_pci_iounmap;
+
+ return 0;
+
+err_pci_iounmap:
+ pci_iounmap(VBOX_DRM_TO_PCI_DEV(vbox->dev), vbox->vbva_buffers);
+ return ret;
+}
+
+static void vbox_accel_fini(struct vbox_private *vbox)
+{
+ vbox_disable_accel(vbox);
+ pci_iounmap(VBOX_DRM_TO_PCI_DEV(vbox->dev), vbox->vbva_buffers);
+}
+
+/** Do we support the 4.3 plus mode hint reporting interface? */
+static bool have_hgsmi_mode_hints(struct vbox_private *vbox)
+{
+ u32 have_hints, have_cursor;
+ int ret;
+
+ ret = VBoxQueryConfHGSMI(vbox->guest_pool,
+ VBOX_VBVA_CONF32_MODE_HINT_REPORTING,
+ &have_hints);
+ if (ret)
+ return false;
+
+ ret = VBoxQueryConfHGSMI(vbox->guest_pool,
+ VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING,
+ &have_cursor);
+ if (ret)
+ return false;
+
+ return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS;
+}
+
+/**
+ * Our refresh timer call-back. Only used for guests without dirty rectangle
+ * support.
+ */
+static void vbox_refresh_timer(struct work_struct *work)
+{
+ struct vbox_private *vbox = container_of(work, struct vbox_private,
+ refresh_work.work);
+ bool have_unblanked = false;
+ struct drm_crtc *crtci;
+
+ if (!vbox->need_refresh_timer)
+ return;
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) {
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtci);
+ if (crtci->enabled && !vbox_crtc->blanked)
+ have_unblanked = true;
+ }
+ if (!have_unblanked)
+ return;
+ /* This forces a full refresh. */
+ vbox_enable_accel(vbox);
+ /* Schedule the next timer iteration. */
+ schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
+}
+
+static bool vbox_check_supported(u16 id)
+{
+ u16 dispi_id;
+
+ vbox_write_ioport(VBE_DISPI_INDEX_ID, id);
+ dispi_id = inw(VBE_DISPI_IOPORT_DATA);
+
+ return dispi_id == id;
+}
+
+/**
+ * Set up our heaps and data exchange buffers in VRAM before handing the rest
+ * to the memory manager.
+ */
+static int vbox_hw_init(struct vbox_private *vbox)
+{
+ int ret = -ENOMEM;
+
+ vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA);
+ vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX);
+
+ DRM_INFO("VRAM %08x\n", vbox->full_vram_size);
+
+ /* Map guest-heap at end of vram */
+ vbox->guest_heap =
+ pci_iomap_range(VBOX_DRM_TO_PCI_DEV(vbox->dev), 0, GUEST_HEAP_OFFSET(vbox),
+ GUEST_HEAP_SIZE);
+ if (!vbox->guest_heap)
+ return -ENOMEM;
+
+ /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */
+ vbox->guest_pool = gen_pool_create(4, -1);
+ if (!vbox->guest_pool)
+ goto err_unmap_guest_heap;
+
+ ret = gen_pool_add_virt(vbox->guest_pool,
+ (unsigned long)vbox->guest_heap,
+ GUEST_HEAP_OFFSET(vbox),
+ GUEST_HEAP_USABLE_SIZE, -1);
+ if (ret)
+ goto err_destroy_guest_pool;
+
+ /* Reduce available VRAM size to reflect the guest heap. */
+ vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox);
+ /* Linux drm represents monitors as a 32-bit array. */
+ VBoxQueryConfHGSMI(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT,
+ &vbox->num_crtcs);
+ vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS);
+
+ if (!have_hgsmi_mode_hints(vbox)) {
+ ret = -ENOTSUPP;
+ goto err_destroy_guest_pool;
+ }
+
+ vbox->last_mode_hints = devm_kcalloc(vbox->dev->dev, vbox->num_crtcs,
+ sizeof(VBVAMODEHINT),
+ GFP_KERNEL);
+ if (!vbox->last_mode_hints) {
+ ret = -ENOMEM;
+ goto err_destroy_guest_pool;
+ }
+
+ ret = vbox_accel_init(vbox);
+ if (ret)
+ goto err_destroy_guest_pool;
+
+ /* Set up the refresh timer for users which do not send dirty rectangles. */
+ INIT_DELAYED_WORK(&vbox->refresh_work, vbox_refresh_timer);
+
+ return 0;
+
+err_destroy_guest_pool:
+ gen_pool_destroy(vbox->guest_pool);
+err_unmap_guest_heap:
+ pci_iounmap(VBOX_DRM_TO_PCI_DEV(vbox->dev), vbox->guest_heap);
+ return ret;
+}
+
+static void vbox_hw_fini(struct vbox_private *vbox)
+{
+ vbox->need_refresh_timer = false;
+ cancel_delayed_work(&vbox->refresh_work);
+ vbox_accel_fini(vbox);
+ gen_pool_destroy(vbox->guest_pool);
+ pci_iounmap(VBOX_DRM_TO_PCI_DEV(vbox->dev), vbox->guest_heap);
+}
+
+#if RTLNX_VER_MIN(4,19,0) || RTLNX_RHEL_MIN(8,3)
+int vbox_driver_load(struct drm_device *dev)
+#else
+int vbox_driver_load(struct drm_device *dev, unsigned long flags)
+#endif
+{
+ struct vbox_private *vbox;
+ int ret = 0;
+
+ if (!vbox_check_supported(VBE_DISPI_ID_HGSMI))
+ return -ENODEV;
+
+ vbox = devm_kzalloc(dev->dev, sizeof(*vbox), GFP_KERNEL);
+ if (!vbox)
+ return -ENOMEM;
+
+ dev->dev_private = vbox;
+ vbox->dev = dev;
+
+ mutex_init(&vbox->hw_mutex);
+
+ ret = vbox_hw_init(vbox);
+ if (ret)
+ return ret;
+
+ ret = vbox_mm_init(vbox);
+ if (ret)
+ goto err_hw_fini;
+
+ drm_mode_config_init(dev);
+
+ dev->mode_config.funcs = (void *)&vbox_mode_funcs;
+ dev->mode_config.min_width = 64;
+ dev->mode_config.min_height = 64;
+ dev->mode_config.preferred_depth = 24;
+ dev->mode_config.max_width = VBE_DISPI_MAX_XRES;
+ dev->mode_config.max_height = VBE_DISPI_MAX_YRES;
+
+ ret = vbox_mode_init(dev);
+ if (ret)
+ goto err_drm_mode_cleanup;
+
+ ret = vbox_irq_init(vbox);
+ if (ret)
+ goto err_mode_fini;
+
+ ret = vbox_fbdev_init(dev);
+ if (ret)
+ goto err_irq_fini;
+
+ return 0;
+
+err_irq_fini:
+ vbox_irq_fini(vbox);
+err_mode_fini:
+ vbox_mode_fini(dev);
+err_drm_mode_cleanup:
+ drm_mode_config_cleanup(dev);
+ vbox_mm_fini(vbox);
+err_hw_fini:
+ vbox_hw_fini(vbox);
+ return ret;
+}
+
+#if RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+void vbox_driver_unload(struct drm_device *dev)
+#else
+int vbox_driver_unload(struct drm_device *dev)
+#endif
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+ vbox_fbdev_fini(dev);
+ vbox_irq_fini(vbox);
+ vbox_mode_fini(dev);
+ drm_mode_config_cleanup(dev);
+ vbox_mm_fini(vbox);
+ vbox_hw_fini(vbox);
+#if RTLNX_VER_MAX(4,11,0) && !RTLNX_RHEL_MAJ_PREREQ(7,5)
+ return 0;
+#endif
+}
+
+/**
+ * @note this is described in the DRM framework documentation. AST does not
+ * have it, but we get an oops on driver unload if it is not present.
+ */
+void vbox_driver_lastclose(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+
+#if RTLNX_VER_MIN(3,16,0) || RTLNX_RHEL_MAJ_PREREQ(7,1)
+ if (vbox->fbdev)
+ drm_fb_helper_restore_fbdev_mode_unlocked(&vbox->fbdev->helper);
+#else
+ drm_modeset_lock_all(dev);
+ if (vbox->fbdev)
+ drm_fb_helper_restore_fbdev_mode(&vbox->fbdev->helper);
+ drm_modeset_unlock_all(dev);
+#endif
+}
+
+int vbox_gem_create(struct drm_device *dev,
+ u32 size, bool iskernel, struct drm_gem_object **obj)
+{
+ struct vbox_bo *vboxbo;
+ int ret;
+
+ *obj = NULL;
+
+ size = roundup(size, PAGE_SIZE);
+ if (size == 0)
+ {
+ DRM_ERROR("bad size\n");
+ return -EINVAL;
+ }
+
+ ret = vbox_bo_create(dev, size, 0, 0, &vboxbo);
+ if (ret) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("failed to allocate GEM object\n");
+ DRM_ERROR("failed to allocate GEM (%d)\n", ret);
+ return ret;
+ }
+
+ *obj = &vboxbo->gem;
+
+ return 0;
+}
+
+int vbox_dumb_create(struct drm_file *file,
+ struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+ int ret;
+ struct drm_gem_object *gobj;
+ u32 handle;
+
+ args->pitch = args->width * ((args->bpp + 7) / 8);
+ args->size = args->pitch * args->height;
+
+ ret = vbox_gem_create(dev, args->size, false, &gobj);
+ if (ret)
+ return ret;
+
+ ret = drm_gem_handle_create(file, gobj, &handle);
+#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ drm_gem_object_put(gobj);
+#else
+ drm_gem_object_put_unlocked(gobj);
+#endif
+ if (ret)
+ return ret;
+
+ args->handle = handle;
+
+ return 0;
+}
+
+#if RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
+int vbox_dumb_destroy(struct drm_file *file,
+ struct drm_device *dev, u32 handle)
+{
+ return drm_gem_handle_delete(file, handle);
+}
+#endif
+
+#if RTLNX_VER_MAX(4,19,0) && !RTLNX_RHEL_MAJ_PREREQ(7,7) && !RTLNX_RHEL_MAJ_PREREQ(8,1) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+static void ttm_bo_put(struct ttm_buffer_object *bo)
+{
+ ttm_bo_unref(&bo);
+}
+#endif
+
+void vbox_gem_free_object(struct drm_gem_object *obj)
+{
+ struct vbox_bo *vbox_bo = gem_to_vbox_bo(obj);
+
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ /* Starting from kernel 5.14, there is a warning appears in dmesg
+ * on attempt to desroy pinned buffer object. Make sure it is unpinned. */
+ while (vbox_bo->bo.pin_count)
+ {
+ int ret;
+ ret = vbox_bo_unpin(vbox_bo);
+ if (ret)
+ {
+ DRM_ERROR("unable to unpin buffer object\n");
+ break;
+ }
+ }
+#endif
+
+ ttm_bo_put(&vbox_bo->bo);
+}
+
+static inline u64 vbox_bo_mmap_offset(struct vbox_bo *bo)
+{
+#if RTLNX_VER_MIN(5,4,0) || RTLNX_RHEL_MIN(8,3) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ return drm_vma_node_offset_addr(&bo->bo.base.vma_node);
+#elif RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,0)
+ return bo->bo.addr_space_offset;
+#else
+ return drm_vma_node_offset_addr(&bo->bo.vma_node);
+#endif /* >= 5.4.0 */
+}
+
+int
+vbox_dumb_mmap_offset(struct drm_file *file,
+ struct drm_device *dev,
+ u32 handle, u64 *offset)
+{
+ struct drm_gem_object *obj;
+ int ret = 0;
+ struct vbox_bo *bo;
+
+ mutex_lock(&dev->struct_mutex);
+#if RTLNX_VER_MIN(4,7,0) || RTLNX_RHEL_MAJ_PREREQ(7,4)
+ obj = drm_gem_object_lookup(file, handle);
+#else
+ obj = drm_gem_object_lookup(dev, file, handle);
+#endif
+ if (!obj) {
+ ret = -ENOENT;
+ goto out_unlock;
+ }
+
+ bo = gem_to_vbox_bo(obj);
+ *offset = vbox_bo_mmap_offset(bo);
+
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ ret = drm_vma_node_allow(&bo->bo.base.vma_node, file);
+ if (ret)
+ {
+ DRM_ERROR("unable to grant previladges to user");
+ }
+#endif
+
+ drm_gem_object_put(obj);
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_mode.c b/src/VBox/Additions/linux/drm/vbox_mode.c
new file mode 100644
index 00000000..bfe13ced
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_mode.c
@@ -0,0 +1,949 @@
+/* $Id: vbox_mode.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ * This file is based on ast_mode.c
+ * Copyright 2012 Red Hat Inc.
+ * Parts based on xf86-video-ast
+ * Copyright (c) 2005 ASPEED Technology Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com,
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+#include "vbox_drv.h"
+#include <linux/export.h>
+#include <drm/drm_crtc_helper.h>
+#if RTLNX_VER_MIN(6,3,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99)
+# include <drm/drm_modeset_helper_vtables.h>
+# include <drm/drm_modeset_helper.h>
+#endif
+#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
+# include <drm/drm_plane_helper.h>
+#endif
+#if RTLNX_VER_MIN(5,1,0) || RTLNX_RHEL_MAJ_PREREQ(8,1)
+# include <drm/drm_probe_helper.h>
+#endif
+
+#if RTLNX_VER_MIN(6,0,0) || RTLNX_RHEL_RANGE(8,8, 8,99) || RTLNX_RHEL_MAJ_PREREQ(9,2) || RTLNX_SUSE_MAJ_PREREQ(15,5)
+# include <drm/drm_edid.h>
+#endif
+
+#include "VBoxVideo.h"
+
+static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
+ u32 handle, u32 width, u32 height,
+ s32 hot_x, s32 hot_y);
+static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y);
+
+/**
+ * Set a graphics mode. Poke any required values into registers, do an HGSMI
+ * mode set and tell the host we support advanced graphics functions.
+ */
+static void vbox_do_modeset(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
+ struct vbox_private *vbox;
+ int width, height, bpp, pitch;
+ u16 flags;
+ s32 x_offset, y_offset;
+
+ vbox = crtc->dev->dev_private;
+ width = mode->hdisplay ? mode->hdisplay : 640;
+ height = mode->vdisplay ? mode->vdisplay : 480;
+#if RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+ bpp = crtc->enabled ? CRTC_FB(crtc)->format->cpp[0] * 8 : 32;
+ pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8;
+#elif RTLNX_VER_MIN(3,3,0)
+ bpp = crtc->enabled ? CRTC_FB(crtc)->bits_per_pixel : 32;
+ pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8;
+#else
+ bpp = crtc->enabled ? CRTC_FB(crtc)->bits_per_pixel : 32;
+ pitch = crtc->enabled ? CRTC_FB(crtc)->pitch : width * bpp / 8;
+#endif
+ x_offset = vbox->single_framebuffer ? crtc->x : vbox_crtc->x_hint;
+ y_offset = vbox->single_framebuffer ? crtc->y : vbox_crtc->y_hint;
+
+ /*
+ * This is the old way of setting graphics modes. It assumed one screen
+ * and a frame-buffer at the start of video RAM. On older versions of
+ * VirtualBox, certain parts of the code still assume that the first
+ * screen is programmed this way, so try to fake it.
+ */
+ if (vbox_crtc->crtc_id == 0 && crtc->enabled &&
+ vbox_crtc->fb_offset / pitch < 0xffff - crtc->y &&
+ vbox_crtc->fb_offset % (bpp / 8) == 0)
+ VBoxVideoSetModeRegisters(
+ width, height, pitch * 8 / bpp,
+#if RTLNX_VER_MIN(4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+ CRTC_FB(crtc)->format->cpp[0] * 8,
+#else
+ CRTC_FB(crtc)->bits_per_pixel,
+#endif
+ 0,
+ vbox_crtc->fb_offset % pitch / bpp * 8 + crtc->x,
+ vbox_crtc->fb_offset / pitch + crtc->y);
+
+ flags = VBVA_SCREEN_F_ACTIVE;
+ flags |= (crtc->enabled && !vbox_crtc->blanked) ?
+ 0 : VBVA_SCREEN_F_BLANK;
+ flags |= vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0;
+ VBoxHGSMIProcessDisplayInfo(vbox->guest_pool, vbox_crtc->crtc_id,
+ x_offset, y_offset, vbox_crtc->fb_offset +
+ crtc->x * bpp / 8 + crtc->y * pitch,
+ pitch, width, height,
+ vbox_crtc->blanked ? 0 : bpp, flags);
+}
+
+static void vbox_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
+ struct vbox_private *vbox = crtc->dev->dev_private;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ vbox_crtc->blanked = false;
+ /* Restart the refresh timer if necessary. */
+ schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ vbox_crtc->blanked = true;
+ break;
+ }
+
+ mutex_lock(&vbox->hw_mutex);
+ vbox_do_modeset(crtc, &crtc->hwmode);
+ mutex_unlock(&vbox->hw_mutex);
+}
+
+static bool vbox_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+/*
+ * Try to map the layout of virtual screens to the range of the input device.
+ * Return true if we need to re-set the crtc modes due to screen offset
+ * changes.
+ */
+static bool vbox_set_up_input_mapping(struct vbox_private *vbox)
+{
+ struct drm_crtc *crtci;
+ struct drm_connector *connectori;
+ struct drm_framebuffer *fb1 = NULL;
+ bool single_framebuffer = true;
+ bool old_single_framebuffer = vbox->single_framebuffer;
+ u16 width = 0, height = 0;
+
+ /*
+ * Are we using an X.Org-style single large frame-buffer for all crtcs?
+ * If so then screen layout can be deduced from the crtc offsets.
+ * Same fall-back if this is the fbdev frame-buffer.
+ */
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) {
+ if (!fb1) {
+ fb1 = CRTC_FB(crtci);
+ if (to_vbox_framebuffer(fb1) == &vbox->fbdev->afb)
+ break;
+ } else if (CRTC_FB(crtci) && fb1 != CRTC_FB(crtci)) {
+ single_framebuffer = false;
+ }
+ }
+ if (single_framebuffer) {
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
+ head) {
+ if (to_vbox_crtc(crtci)->crtc_id != 0)
+ continue;
+
+ if (!CRTC_FB(crtci))
+ break;
+ vbox->single_framebuffer = true;
+ vbox->input_mapping_width = CRTC_FB(crtci)->width;
+ vbox->input_mapping_height = CRTC_FB(crtci)->height;
+ return old_single_framebuffer !=
+ vbox->single_framebuffer;
+ }
+ }
+ /* Otherwise calculate the total span of all screens. */
+ list_for_each_entry(connectori, &vbox->dev->mode_config.connector_list,
+ head) {
+ struct vbox_connector *vbox_connector =
+ to_vbox_connector(connectori);
+ struct vbox_crtc *vbox_crtc = vbox_connector->vbox_crtc;
+
+ width = max_t(u16, width, vbox_crtc->x_hint +
+ vbox_connector->mode_hint.width);
+ height = max_t(u16, height, vbox_crtc->y_hint +
+ vbox_connector->mode_hint.height);
+ }
+
+ vbox->single_framebuffer = false;
+ vbox->input_mapping_width = width;
+ vbox->input_mapping_height = height;
+
+ return old_single_framebuffer != vbox->single_framebuffer;
+}
+
+static int vbox_crtc_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *old_fb,
+ struct drm_framebuffer *new_fb,
+ int x, int y)
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
+ struct drm_gem_object *obj;
+ struct vbox_framebuffer *vbox_fb;
+ struct vbox_bo *bo;
+ int ret;
+ u64 gpu_addr;
+
+ vbox_fb = to_vbox_framebuffer(new_fb);
+ obj = vbox_fb->obj;
+ bo = gem_to_vbox_bo(obj);
+
+ ret = vbox_bo_reserve(bo, false);
+ if (ret)
+ return ret;
+
+ ret = vbox_bo_pin(bo, VBOX_MEM_TYPE_VRAM, &gpu_addr);
+ vbox_bo_unreserve(bo);
+ if (ret)
+ return ret;
+
+ /* Unpin the previous fb. Do this after the new one has been pinned rather
+ * than before and re-pinning it on failure in case that fails too. */
+ if (old_fb) {
+ vbox_fb = to_vbox_framebuffer(old_fb);
+ obj = vbox_fb->obj;
+ bo = gem_to_vbox_bo(obj);
+ ret = vbox_bo_reserve(bo, false);
+ /* This should never fail, as no one else should be accessing it and we
+ * should be running under the modeset locks. */
+ if (!ret) {
+ vbox_bo_unpin(bo);
+ vbox_bo_unreserve(bo);
+ }
+ else
+ {
+ DRM_ERROR("unable to lock buffer object: error %d\n", ret);
+ }
+ }
+
+ if (&vbox->fbdev->afb == vbox_fb)
+ vbox_fbdev_set_base(vbox, gpu_addr);
+
+ vbox_crtc->fb_offset = gpu_addr;
+ if (vbox_set_up_input_mapping(vbox)) {
+ struct drm_crtc *crtci;
+
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
+ head) {
+ vbox_do_modeset(crtci, &crtci->mode);
+ }
+ }
+
+ return 0;
+}
+
+static int vbox_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y, struct drm_framebuffer *old_fb)
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ int ret = vbox_crtc_set_base(crtc, old_fb, CRTC_FB(crtc), x, y);
+ if (ret)
+ return ret;
+ mutex_lock(&vbox->hw_mutex);
+ vbox_do_modeset(crtc, mode);
+ VBoxHGSMIUpdateInputMapping(vbox->guest_pool, 0, 0,
+ vbox->input_mapping_width,
+ vbox->input_mapping_height);
+ mutex_unlock(&vbox->hw_mutex);
+
+ return ret;
+}
+
+static int vbox_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+#if RTLNX_VER_MIN(4,12,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags,
+ struct drm_modeset_acquire_ctx *ctx)
+#elif RTLNX_VER_MIN(3,12,0) || RTLNX_RHEL_MAJ_PREREQ(7,0)
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags)
+#else
+ struct drm_pending_vblank_event *event)
+#endif
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ struct drm_device *drm = vbox->dev;
+ unsigned long flags;
+ int rc;
+
+ rc = vbox_crtc_set_base(crtc, CRTC_FB(crtc), fb, 0, 0);
+ if (rc)
+ return rc;
+
+ mutex_lock(&vbox->hw_mutex);
+ vbox_do_modeset(crtc, &crtc->mode);
+ mutex_unlock(&vbox->hw_mutex);
+
+ spin_lock_irqsave(&drm->event_lock, flags);
+
+ if (event)
+#if RTLNX_VER_MIN(3,19,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
+ drm_crtc_send_vblank_event(crtc, event);
+#else
+ drm_send_vblank_event(drm, -1, event);
+#endif
+
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+
+ return 0;
+}
+
+static void vbox_crtc_disable(struct drm_crtc *crtc)
+{
+}
+
+static void vbox_crtc_prepare(struct drm_crtc *crtc)
+{
+}
+
+static void vbox_crtc_commit(struct drm_crtc *crtc)
+{
+}
+
+static const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = {
+ .dpms = vbox_crtc_dpms,
+ .mode_fixup = vbox_crtc_mode_fixup,
+ .mode_set = vbox_crtc_mode_set,
+ .disable = vbox_crtc_disable,
+ .prepare = vbox_crtc_prepare,
+ .commit = vbox_crtc_commit,
+};
+
+static void vbox_crtc_reset(struct drm_crtc *crtc)
+{
+}
+
+static void vbox_crtc_destroy(struct drm_crtc *crtc)
+{
+ drm_crtc_cleanup(crtc);
+ kfree(crtc);
+}
+
+static const struct drm_crtc_funcs vbox_crtc_funcs = {
+ .cursor_move = vbox_cursor_move,
+ .cursor_set2 = vbox_cursor_set2,
+ .reset = vbox_crtc_reset,
+ .set_config = drm_crtc_helper_set_config,
+ /* .gamma_set = vbox_crtc_gamma_set, */
+ .page_flip = vbox_crtc_page_flip,
+ .destroy = vbox_crtc_destroy,
+};
+
+static struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i)
+{
+ struct vbox_crtc *vbox_crtc;
+
+ vbox_crtc = kzalloc(sizeof(*vbox_crtc), GFP_KERNEL);
+ if (!vbox_crtc)
+ return NULL;
+
+ vbox_crtc->crtc_id = i;
+
+ drm_crtc_init(dev, &vbox_crtc->base, &vbox_crtc_funcs);
+ drm_mode_crtc_set_gamma_size(&vbox_crtc->base, 256);
+ drm_crtc_helper_add(&vbox_crtc->base, &vbox_crtc_helper_funcs);
+
+ return vbox_crtc;
+}
+
+static void vbox_encoder_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+ kfree(encoder);
+}
+
+#if RTLNX_VER_MAX(3,13,0) && !RTLNX_RHEL_MAJ_PREREQ(7,1)
+static struct drm_encoder *drm_encoder_find(struct drm_device *dev, u32 id)
+{
+ struct drm_mode_object *mo;
+
+ mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
+ return mo ? obj_to_encoder(mo) : NULL;
+}
+#endif
+
+static struct drm_encoder *vbox_best_single_encoder(struct drm_connector
+ *connector)
+{
+#if RTLNX_VER_MIN(5,5,0) || RTLNX_RHEL_MIN(8,3) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ struct drm_encoder *encoder;
+
+ /* There is only one encoder per connector */
+ drm_connector_for_each_possible_encoder(connector, encoder)
+ return encoder;
+#else /* < 5.5 || RHEL < 8.3 */
+ int enc_id = connector->encoder_ids[0];
+
+ /* pick the encoder ids */
+ if (enc_id)
+# if RTLNX_VER_MIN(4,15,0) || RTLNX_RHEL_MAJ_PREREQ(7,6) || (defined(CONFIG_SUSE_VERSION) && RTLNX_VER_MIN(4,12,0))
+ return drm_encoder_find(connector->dev, NULL, enc_id);
+# else
+ return drm_encoder_find(connector->dev, enc_id);
+# endif
+#endif /* < 5.5 || RHEL < 8.3 */
+ return NULL;
+}
+
+static const struct drm_encoder_funcs vbox_enc_funcs = {
+ .destroy = vbox_encoder_destroy,
+};
+
+static void vbox_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool vbox_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void vbox_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void vbox_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void vbox_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static const struct drm_encoder_helper_funcs vbox_enc_helper_funcs = {
+ .dpms = vbox_encoder_dpms,
+ .mode_fixup = vbox_mode_fixup,
+ .prepare = vbox_encoder_prepare,
+ .commit = vbox_encoder_commit,
+ .mode_set = vbox_encoder_mode_set,
+};
+
+static struct drm_encoder *vbox_encoder_init(struct drm_device *dev,
+ unsigned int i)
+{
+ struct vbox_encoder *vbox_encoder;
+
+ vbox_encoder = kzalloc(sizeof(*vbox_encoder), GFP_KERNEL);
+ if (!vbox_encoder)
+ return NULL;
+
+ drm_encoder_init(dev, &vbox_encoder->base, &vbox_enc_funcs,
+#if RTLNX_VER_MIN(4,5,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
+ DRM_MODE_ENCODER_DAC, NULL);
+#else
+ DRM_MODE_ENCODER_DAC);
+#endif
+ drm_encoder_helper_add(&vbox_encoder->base, &vbox_enc_helper_funcs);
+
+ vbox_encoder->base.possible_crtcs = 1 << i;
+ return &vbox_encoder->base;
+}
+
+/**
+ * Generate EDID data with a mode-unique serial number for the virtual
+ * monitor to try to persuade Unity that different modes correspond to
+ * different monitors and it should not try to force the same resolution on
+ * them.
+ */
+static void vbox_set_edid(struct drm_connector *connector, int width,
+ int height)
+{
+ enum { EDID_SIZE = 128 };
+ unsigned char edid[EDID_SIZE] = {
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */
+ 0x58, 0x58, /* manufacturer (VBX) */
+ 0x00, 0x00, /* product code */
+ 0x00, 0x00, 0x00, 0x00, /* serial number goes here */
+ 0x01, /* week of manufacture */
+ 0x00, /* year of manufacture */
+ 0x01, 0x03, /* EDID version */
+ 0x80, /* capabilities - digital */
+ 0x00, /* horiz. res in cm, zero for projectors */
+ 0x00, /* vert. res in cm */
+ 0x78, /* display gamma (120 == 2.2). */
+ 0xEE, /* features (standby, suspend, off, RGB, std */
+ /* colour space, preferred timing mode) */
+ 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
+ /* chromaticity for standard colour space. */
+ 0x00, 0x00, 0x00, /* no default timings */
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, /* no standard timings */
+ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02,
+ 0x02, 0x02,
+ /* descriptor block 1 goes below */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* descriptor block 2, monitor ranges */
+ 0x00, 0x00, 0x00, 0xFD, 0x00,
+ 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20,
+ 0x20, 0x20,
+ /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
+ 0x20,
+ /* descriptor block 3, monitor name */
+ 0x00, 0x00, 0x00, 0xFC, 0x00,
+ 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r',
+ '\n',
+ /* descriptor block 4: dummy data */
+ 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20,
+ 0x00, /* number of extensions */
+ 0x00 /* checksum goes here */
+ };
+ int clock = (width + 6) * (height + 6) * 60 / 10000;
+ unsigned int i, sum = 0;
+
+ edid[12] = width & 0xff;
+ edid[13] = width >> 8;
+ edid[14] = height & 0xff;
+ edid[15] = height >> 8;
+ edid[54] = clock & 0xff;
+ edid[55] = clock >> 8;
+ edid[56] = width & 0xff;
+ edid[58] = (width >> 4) & 0xf0;
+ edid[59] = height & 0xff;
+ edid[61] = (height >> 4) & 0xf0;
+ for (i = 0; i < EDID_SIZE - 1; ++i)
+ sum += edid[i];
+ edid[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF;
+#if RTLNX_VER_MIN(4,19,0) || RTLNX_RHEL_MAJ_PREREQ(7,7) || RTLNX_RHEL_MAJ_PREREQ(8,1) || RTLNX_SUSE_MAJ_PREREQ(15,1) || RTLNX_SUSE_MAJ_PREREQ(12,5)
+ drm_connector_update_edid_property(connector, (struct edid *)edid);
+#else
+ drm_mode_connector_update_edid_property(connector, (struct edid *)edid);
+#endif
+}
+
+static int vbox_get_modes(struct drm_connector *connector)
+{
+ struct vbox_connector *vbox_connector = NULL;
+ struct drm_display_mode *mode = NULL;
+ struct vbox_private *vbox = NULL;
+ unsigned int num_modes = 0;
+ int preferred_width, preferred_height;
+
+ vbox_connector = to_vbox_connector(connector);
+ vbox = connector->dev->dev_private;
+ /*
+ * Heuristic: we do not want to tell the host that we support dynamic
+ * resizing unless we feel confident that the user space client using
+ * the video driver can handle hot-plug events. So the first time modes
+ * are queried after a "master" switch we tell the host that we do not,
+ * and immediately after we send the client a hot-plug notification as
+ * a test to see if they will respond and query again.
+ * That is also the reason why capabilities are reported to the host at
+ * this place in the code rather than elsewhere.
+ * We need to report the flags location before reporting the IRQ
+ * capability.
+ */
+ VBoxHGSMIReportFlagsLocation(vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) +
+ HOST_FLAGS_OFFSET);
+ if (vbox_connector->vbox_crtc->crtc_id == 0)
+ vbox_report_caps(vbox);
+ if (!vbox->initial_mode_queried) {
+ if (vbox_connector->vbox_crtc->crtc_id == 0) {
+ vbox->initial_mode_queried = true;
+ vbox_report_hotplug(vbox);
+ }
+ return drm_add_modes_noedid(connector, 800, 600);
+ }
+ /* Also assume that a client which supports hot-plugging also knows
+ * how to update the screen in a way we can use, the only known
+ * relevent client which cannot is Plymouth in Ubuntu 14.04. */
+ vbox->need_refresh_timer = false;
+ num_modes = drm_add_modes_noedid(connector, 2560, 1600);
+ preferred_width = vbox_connector->mode_hint.width ?
+ vbox_connector->mode_hint.width : 1024;
+ preferred_height = vbox_connector->mode_hint.height ?
+ vbox_connector->mode_hint.height : 768;
+ mode = drm_cvt_mode(connector->dev, preferred_width, preferred_height,
+ 60, false, false, false);
+ if (mode) {
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+ ++num_modes;
+ }
+ vbox_set_edid(connector, preferred_width, preferred_height);
+
+#if RTLNX_VER_MIN(3,19,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
+ if (vbox_connector->vbox_crtc->x_hint != -1)
+ drm_object_property_set_value(&connector->base,
+ vbox->dev->mode_config.suggested_x_property,
+ vbox_connector->vbox_crtc->x_hint);
+ else
+ drm_object_property_set_value(&connector->base,
+ vbox->dev->mode_config.suggested_x_property, 0);
+
+ if (vbox_connector->vbox_crtc->y_hint != -1)
+ drm_object_property_set_value(&connector->base,
+ vbox->dev->mode_config.suggested_y_property,
+ vbox_connector->vbox_crtc->y_hint);
+ else
+ drm_object_property_set_value(&connector->base,
+ vbox->dev->mode_config.suggested_y_property, 0);
+#endif
+
+ return num_modes;
+}
+
+#if RTLNX_VER_MAX(3,14,0) && !RTLNX_RHEL_MAJ_PREREQ(7,1)
+static int vbox_mode_valid(struct drm_connector *connector,
+#else
+static enum drm_mode_status vbox_mode_valid(struct drm_connector *connector,
+#endif
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static void vbox_connector_destroy(struct drm_connector *connector)
+{
+#if RTLNX_VER_MAX(3,17,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
+ drm_sysfs_connector_remove(connector);
+#else
+ drm_connector_unregister(connector);
+#endif
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static enum drm_connector_status
+vbox_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct vbox_connector *vbox_connector;
+
+ vbox_connector = to_vbox_connector(connector);
+
+ return vbox_connector->mode_hint.disconnected ?
+ connector_status_disconnected : connector_status_connected;
+}
+
+static int vbox_fill_modes(struct drm_connector *connector, u32 max_x,
+ u32 max_y)
+{
+ struct vbox_connector *vbox_connector;
+ struct drm_device *dev;
+ struct drm_display_mode *mode, *iterator;
+
+ vbox_connector = to_vbox_connector(connector);
+ dev = vbox_connector->base.dev;
+ list_for_each_entry_safe(mode, iterator, &connector->modes, head) {
+ list_del(&mode->head);
+ drm_mode_destroy(dev, mode);
+ }
+
+ return drm_helper_probe_single_connector_modes(connector, max_x, max_y);
+}
+
+static const struct drm_connector_helper_funcs vbox_connector_helper_funcs = {
+ .mode_valid = vbox_mode_valid,
+ .get_modes = vbox_get_modes,
+ .best_encoder = vbox_best_single_encoder,
+};
+
+static const struct drm_connector_funcs vbox_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = vbox_connector_detect,
+ .fill_modes = vbox_fill_modes,
+ .destroy = vbox_connector_destroy,
+};
+
+static int vbox_connector_init(struct drm_device *dev,
+ struct vbox_crtc *vbox_crtc,
+ struct drm_encoder *encoder)
+{
+ struct vbox_connector *vbox_connector;
+ struct drm_connector *connector;
+
+ vbox_connector = kzalloc(sizeof(*vbox_connector), GFP_KERNEL);
+ if (!vbox_connector)
+ return -ENOMEM;
+
+ connector = &vbox_connector->base;
+ vbox_connector->vbox_crtc = vbox_crtc;
+
+ drm_connector_init(dev, connector, &vbox_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA);
+ drm_connector_helper_add(connector, &vbox_connector_helper_funcs);
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+#if RTLNX_VER_MIN(3,19,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
+ drm_mode_create_suggested_offset_properties(dev);
+ drm_object_attach_property(&connector->base,
+ dev->mode_config.suggested_x_property, 0);
+ drm_object_attach_property(&connector->base,
+ dev->mode_config.suggested_y_property, 0);
+#endif
+#if RTLNX_VER_MAX(3,17,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
+ drm_sysfs_connector_add(connector);
+#else
+ drm_connector_register(connector);
+#endif
+
+#if RTLNX_VER_MIN(4,19,0) || RTLNX_RHEL_MAJ_PREREQ(7,7) || RTLNX_RHEL_MAJ_PREREQ(8,1) || RTLNX_SUSE_MAJ_PREREQ(15,1) || RTLNX_SUSE_MAJ_PREREQ(12,5)
+ drm_connector_attach_encoder(connector, encoder);
+#else
+ drm_mode_connector_attach_encoder(connector, encoder);
+#endif
+
+ return 0;
+}
+
+int vbox_mode_init(struct drm_device *dev)
+{
+ struct vbox_private *vbox = dev->dev_private;
+ struct drm_encoder *encoder;
+ struct vbox_crtc *vbox_crtc;
+ unsigned int i;
+ int ret;
+
+ /* vbox_cursor_init(dev); */
+ for (i = 0; i < vbox->num_crtcs; ++i) {
+ vbox_crtc = vbox_crtc_init(dev, i);
+ if (!vbox_crtc)
+ return -ENOMEM;
+ encoder = vbox_encoder_init(dev, i);
+ if (!encoder)
+ return -ENOMEM;
+ ret = vbox_connector_init(dev, vbox_crtc, encoder);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void vbox_mode_fini(struct drm_device *dev)
+{
+ /* vbox_cursor_fini(dev); */
+}
+
+/**
+ * Copy the ARGB image and generate the mask, which is needed in case the host
+ * does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set
+ * if the corresponding alpha value in the ARGB image is greater than 0xF0.
+ */
+static void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height,
+ size_t mask_size)
+{
+ size_t line_size = (width + 7) / 8;
+ u32 i, j;
+
+ memcpy(dst + mask_size, src, width * height * 4);
+ for (i = 0; i < height; ++i)
+ for (j = 0; j < width; ++j)
+ if (((u32 *)src)[i * width + j] > 0xf0000000)
+ dst[i * line_size + j / 8] |= (0x80 >> (j % 8));
+}
+
+static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
+ u32 handle, u32 width, u32 height,
+ s32 hot_x, s32 hot_y)
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
+ struct ttm_bo_kmap_obj uobj_map;
+ size_t data_size, mask_size;
+ struct drm_gem_object *obj;
+ u32 flags, caps = 0;
+ struct vbox_bo *bo;
+ bool src_isiomem;
+ u8 *dst = NULL;
+ u8 *src;
+ int ret;
+
+ if (!handle) {
+ bool cursor_enabled = false;
+ struct drm_crtc *crtci;
+
+ /* Hide cursor. */
+ vbox_crtc->cursor_enabled = false;
+ list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list,
+ head) {
+ if (to_vbox_crtc(crtci)->cursor_enabled)
+ cursor_enabled = true;
+ }
+
+ if (!cursor_enabled)
+ VBoxHGSMIUpdatePointerShape(vbox->guest_pool, 0, 0, 0,
+ 0, 0, NULL, 0);
+ return 0;
+ }
+
+ vbox_crtc->cursor_enabled = true;
+
+ if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT ||
+ width == 0 || height == 0)
+ return -EINVAL;
+ ret = VBoxQueryConfHGSMI(vbox->guest_pool,
+ VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps);
+ if (ret)
+ return ret == VERR_NO_MEMORY ? -ENOMEM : -EINVAL;
+
+ if (!(caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE)) {
+ /*
+ * -EINVAL means cursor_set2() not supported, -EAGAIN means
+ * retry at once.
+ */
+ return -EBUSY;
+ }
+
+#if RTLNX_VER_MIN(4,7,0) || RTLNX_RHEL_MAJ_PREREQ(7,4)
+ obj = drm_gem_object_lookup(file_priv, handle);
+#else
+ obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
+#endif
+ if (!obj) {
+ DRM_ERROR("Cannot find cursor object %x for crtc\n", handle);
+ return -ENOENT;
+ }
+
+ bo = gem_to_vbox_bo(obj);
+ ret = vbox_bo_reserve(bo, false);
+ if (ret)
+ goto out_unref_obj;
+
+ /*
+ * The mask must be calculated based on the alpha
+ * channel, one bit per ARGB word, and must be 32-bit
+ * padded.
+ */
+ mask_size = ((width + 7) / 8 * height + 3) & ~3;
+ data_size = width * height * 4 + mask_size;
+ vbox->cursor_hot_x = hot_x;
+ vbox->cursor_hot_y = hot_y;
+ vbox->cursor_width = width;
+ vbox->cursor_height = height;
+ vbox->cursor_data_size = data_size;
+ dst = vbox->cursor_data;
+
+#if RTLNX_VER_MIN(6,4,0)
+ /* Make sure bo is in SYSTEM (main) memory, so we can access it directly. */
+ ret = vbox_bo_pin(bo, VBOX_MEM_TYPE_SYSTEM, NULL);
+ if (ret)
+ {
+ DRM_ERROR("cannot pin bo to main memory\n");
+ goto out_bo_unpin;
+ }
+#endif
+
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ ret = ttm_bo_kmap(&bo->bo, 0, VBOX_BO_RESOURCE_NUM_PAGES(bo->bo.resource), &uobj_map);
+#elif RTLNX_VER_MIN(5,12,0) || RTLNX_RHEL_MAJ_PREREQ(8,5)
+ ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.mem.num_pages, &uobj_map);
+#else
+ ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &uobj_map);
+#endif
+ if (ret) {
+ vbox->cursor_data_size = 0;
+ goto out_unreserve_bo;
+ }
+
+ src = ttm_kmap_obj_virtual(&uobj_map, &src_isiomem);
+ if (src_isiomem) {
+ DRM_ERROR("src cursor bo not in main memory\n");
+ ret = -EIO;
+ goto out_unmap_bo;
+ }
+
+ copy_cursor_image(src, dst, width, height, mask_size);
+
+ flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE |
+ VBOX_MOUSE_POINTER_ALPHA;
+ ret = VBoxHGSMIUpdatePointerShape(vbox->guest_pool, flags,
+ vbox->cursor_hot_x, vbox->cursor_hot_y,
+ width, height, dst, data_size);
+ ret = ret == VINF_SUCCESS ? 0 : ret == VERR_NO_MEMORY ? -ENOMEM :
+ ret == VERR_NOT_SUPPORTED ? -EBUSY : -EINVAL;
+
+out_unmap_bo:
+ ttm_bo_kunmap(&uobj_map);
+#if RTLNX_VER_MIN(6,4,0)
+out_bo_unpin:
+ vbox_bo_unpin(bo);
+#endif
+out_unreserve_bo:
+ vbox_bo_unreserve(bo);
+out_unref_obj:
+#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ drm_gem_object_put(obj);
+#else
+ drm_gem_object_put_unlocked(obj);
+#endif
+
+ return ret;
+}
+
+static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+ struct vbox_private *vbox = crtc->dev->dev_private;
+ s32 crtc_x =
+ vbox->single_framebuffer ? crtc->x : to_vbox_crtc(crtc)->x_hint;
+ s32 crtc_y =
+ vbox->single_framebuffer ? crtc->y : to_vbox_crtc(crtc)->y_hint;
+ int ret;
+
+ x += vbox->cursor_hot_x;
+ y += vbox->cursor_hot_y;
+ if (x + crtc_x < 0 || y + crtc_y < 0 ||
+ x + crtc_x >= vbox->input_mapping_width ||
+ y + crtc_y >= vbox->input_mapping_width ||
+ vbox->cursor_data_size == 0)
+ return 0;
+ ret = VBoxHGSMICursorPosition(vbox->guest_pool, true, x + crtc_x,
+ y + crtc_y, NULL, NULL);
+ return ret == VINF_SUCCESS ? 0 : ret == VERR_NO_MEMORY ? -ENOMEM : ret ==
+ VERR_NOT_SUPPORTED ? -EBUSY : -EINVAL;
+}
diff --git a/src/VBox/Additions/linux/drm/vbox_prime.c b/src/VBox/Additions/linux/drm/vbox_prime.c
new file mode 100644
index 00000000..87cf0a99
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_prime.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ * This file is based on qxl_prime.c
+ * Copyright 2017 Canonical
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Andreas Pokorny
+ */
+
+#include "vbox_drv.h"
+
+/*
+ * Based on qxl_prime.c:
+ * Empty Implementations as there should not be any other driver for a virtual
+ * device that might share buffers with vboxvideo
+ */
+
+int vbox_gem_prime_pin(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return -ENOSYS;
+}
+
+void vbox_gem_prime_unpin(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+}
+
+struct sg_table *vbox_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+#if RTLNX_VER_MAX(3,18,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
+struct drm_gem_object *vbox_gem_prime_import_sg_table(
+ struct drm_device *dev, size_t size, struct sg_table *table)
+#else
+struct drm_gem_object *vbox_gem_prime_import_sg_table(
+ struct drm_device *dev, struct dma_buf_attachment *attach,
+ struct sg_table *table)
+#endif
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+void *vbox_gem_prime_vmap(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+void vbox_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+ WARN_ONCE(1, "not implemented");
+}
+
+#if RTLNX_VER_MAX(6,6,0) && !RTLNX_RHEL_RANGE(9,4, 9,99)
+int vbox_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *area)
+{
+ WARN_ONCE(1, "not implemented");
+ return -ENOSYS;
+}
+#endif
diff --git a/src/VBox/Additions/linux/drm/vbox_ttm.c b/src/VBox/Additions/linux/drm/vbox_ttm.c
new file mode 100644
index 00000000..d020aa3f
--- /dev/null
+++ b/src/VBox/Additions/linux/drm/vbox_ttm.c
@@ -0,0 +1,843 @@
+/* $Id: vbox_ttm.c $ */
+/** @file
+ * VirtualBox Additions Linux kernel video driver
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ * This file is based on ast_ttm.c
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ *
+ * Authors: Dave Airlie <airlied@redhat.com>
+ * Michael Thayer <michael.thayer@oracle.com>
+ */
+#include "vbox_drv.h"
+
+#if RTLNX_VER_MIN(6,3,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_MAJ_PREREQ(9,3)
+# include <drm/ttm/ttm_tt.h>
+#endif
+
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_MAJ_PREREQ(8,5)
+# include <drm/drm_gem.h>
+# include <drm/drm_gem_ttm_helper.h>
+# include <drm/drm_gem_vram_helper.h>
+#else
+# include <drm/ttm/ttm_page_alloc.h>
+#endif
+
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+# include <drm/ttm/ttm_range_manager.h>
+#endif
+
+#if RTLNX_VER_MAX(3,18,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
+#define PLACEMENT_FLAGS(placement) (placement)
+#else
+#define PLACEMENT_FLAGS(placement) ((placement).flags)
+#endif
+
+
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+static inline struct vbox_private *vbox_bdev(struct ttm_device *bd)
+#else
+static inline struct vbox_private *vbox_bdev(struct ttm_bo_device *bd)
+#endif
+{
+ return container_of(bd, struct vbox_private, ttm.bdev);
+}
+
+#if RTLNX_VER_MAX(5,0,0) && !RTLNX_RHEL_MAJ_PREREQ(7,7) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
+static int vbox_ttm_mem_global_init(struct drm_global_reference *ref)
+{
+ return ttm_mem_global_init(ref->object);
+}
+
+static void vbox_ttm_mem_global_release(struct drm_global_reference *ref)
+{
+ ttm_mem_global_release(ref->object);
+}
+
+/**
+ * Adds the vbox memory manager object/structures to the global memory manager.
+ */
+static int vbox_ttm_global_init(struct vbox_private *vbox)
+{
+ struct drm_global_reference *global_ref;
+ int ret;
+
+#if RTLNX_VER_MAX(5,0,0)
+ global_ref = &vbox->ttm.mem_global_ref;
+ global_ref->global_type = DRM_GLOBAL_TTM_MEM;
+ global_ref->size = sizeof(struct ttm_mem_global);
+ global_ref->init = &vbox_ttm_mem_global_init;
+ global_ref->release = &vbox_ttm_mem_global_release;
+ ret = drm_global_item_ref(global_ref);
+ if (ret) {
+ DRM_ERROR("Failed setting up TTM memory subsystem.\n");
+ return ret;
+ }
+
+ vbox->ttm.bo_global_ref.mem_glob = vbox->ttm.mem_global_ref.object;
+#endif
+ global_ref = &vbox->ttm.bo_global_ref.ref;
+ global_ref->global_type = DRM_GLOBAL_TTM_BO;
+ global_ref->size = sizeof(struct ttm_bo_global);
+ global_ref->init = &ttm_bo_global_init;
+ global_ref->release = &ttm_bo_global_release;
+
+ ret = drm_global_item_ref(global_ref);
+ if (ret) {
+ DRM_ERROR("Failed setting up TTM BO subsystem.\n");
+#if RTLNX_VER_MAX(5,0,0)
+ drm_global_item_unref(&vbox->ttm.mem_global_ref);
+#endif
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * Removes the vbox memory manager object from the global memory manager.
+ */
+static void vbox_ttm_global_release(struct vbox_private *vbox)
+{
+ drm_global_item_unref(&vbox->ttm.bo_global_ref.ref);
+ drm_global_item_unref(&vbox->ttm.mem_global_ref);
+}
+#endif
+
+static void vbox_bo_ttm_destroy(struct ttm_buffer_object *tbo)
+{
+ struct vbox_bo *bo;
+
+ bo = container_of(tbo, struct vbox_bo, bo);
+
+ drm_gem_object_release(&bo->gem);
+ kfree(bo);
+}
+
+static bool vbox_ttm_bo_is_vbox_bo(struct ttm_buffer_object *bo)
+{
+ if (bo->destroy == &vbox_bo_ttm_destroy)
+ return true;
+
+ return false;
+}
+
+#if RTLNX_VER_MAX(5,10,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+static int
+vbox_bo_init_mem_type(struct ttm_bo_device *bdev, u32 type,
+ struct ttm_mem_type_manager *man)
+{
+ switch (type) {
+ case TTM_PL_SYSTEM:
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_MASK_CACHING;
+ man->default_caching = TTM_PL_FLAG_CACHED;
+ break;
+ case TTM_PL_VRAM:
+ man->func = &ttm_bo_manager_func;
+ man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC;
+ man->default_caching = TTM_PL_FLAG_WC;
+ break;
+ default:
+ DRM_ERROR("Unsupported memory type %u\n", (unsigned int)type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#endif
+
+static void
+vbox_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
+{
+ struct vbox_bo *vboxbo = vbox_bo(bo);
+
+ if (!vbox_ttm_bo_is_vbox_bo(bo))
+ return;
+
+ vbox_ttm_placement(vboxbo, VBOX_MEM_TYPE_SYSTEM);
+ *pl = vboxbo->placement;
+}
+
+#if RTLNX_VER_MAX(5,14,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
+static int vbox_bo_verify_access(struct ttm_buffer_object *bo,
+ struct file *filp)
+{
+ return 0;
+}
+#endif
+
+#if RTLNX_VER_MAX(5,10,0) && !RTLNX_RHEL_RANGE(8,5, 8,99)
+static int vbox_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
+ struct ttm_mem_reg *mem)
+{
+ struct vbox_private *vbox = vbox_bdev(bdev);
+ struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
+
+ mem->bus.addr = NULL;
+ mem->bus.offset = 0;
+ mem->bus.size = mem->num_pages << PAGE_SHIFT;
+ mem->bus.base = 0;
+ mem->bus.is_iomem = false;
+ if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
+ return -EINVAL;
+ switch (mem->mem_type) {
+ case TTM_PL_SYSTEM:
+ /* system memory */
+ return 0;
+ case TTM_PL_VRAM:
+ mem->bus.offset = mem->start << PAGE_SHIFT;
+ mem->bus.base = pci_resource_start(vbox->dev->pdev, 0);
+ mem->bus.is_iomem = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+#else
+# if RTLNX_VER_MAX(5,13,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
+static int vbox_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
+ struct ttm_resource *mem)
+# else /* > 5.13.0 */
+static int vbox_ttm_io_mem_reserve(struct ttm_device *bdev,
+ struct ttm_resource *mem)
+# endif /* > 5.13.0 */
+{
+ struct vbox_private *vbox = vbox_bdev(bdev);
+ mem->bus.addr = NULL;
+ mem->bus.offset = 0;
+# if RTLNX_VER_MAX(5,12,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ mem->size = mem->num_pages << PAGE_SHIFT;
+# endif
+ mem->start = 0;
+ mem->bus.is_iomem = false;
+ switch (mem->mem_type) {
+ case TTM_PL_SYSTEM:
+ /* system memory */
+ return 0;
+ case TTM_PL_VRAM:
+# if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ mem->bus.caching = ttm_write_combined;
+# endif
+# if RTLNX_VER_MIN(5,10,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ mem->bus.offset = (mem->start << PAGE_SHIFT) + pci_resource_start(VBOX_DRM_TO_PCI_DEV(vbox->dev), 0);
+# else
+ mem->bus.offset = mem->start << PAGE_SHIFT;
+ mem->start = pci_resource_start(VBOX_DRM_TO_PCI_DEV(vbox->dev), 0);
+# endif
+ mem->bus.is_iomem = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+#endif
+
+
+
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+static void vbox_ttm_io_mem_free(struct ttm_device *bdev,
+ struct ttm_resource *mem)
+{
+}
+#elif RTLNX_VER_MIN(5,10,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+static void vbox_ttm_io_mem_free(struct ttm_bo_device *bdev,
+ struct ttm_resource *mem)
+{
+}
+#else
+static void vbox_ttm_io_mem_free(struct ttm_bo_device *bdev,
+ struct ttm_mem_reg *mem)
+{
+}
+#endif
+
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+static void vbox_ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *tt)
+{
+ ttm_tt_fini(tt);
+ kfree(tt);
+}
+#elif RTLNX_VER_MIN(5,10,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+static void vbox_ttm_tt_destroy(struct ttm_bo_device *bdev, struct ttm_tt *tt)
+{
+ ttm_tt_fini(tt);
+ kfree(tt);
+}
+#else
+static void vbox_ttm_backend_destroy(struct ttm_tt *tt)
+{
+ ttm_tt_fini(tt);
+ kfree(tt);
+}
+
+static struct ttm_backend_func vbox_tt_backend_func = {
+ .destroy = &vbox_ttm_backend_destroy,
+};
+#endif
+
+#if RTLNX_VER_MAX(4,17,0) && !RTLNX_RHEL_MAJ_PREREQ(7,6) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+static struct ttm_tt *vbox_ttm_tt_create(struct ttm_bo_device *bdev,
+ unsigned long size,
+ u32 page_flags,
+ struct page *dummy_read_page)
+#else
+static struct ttm_tt *vbox_ttm_tt_create(struct ttm_buffer_object *bo,
+ u32 page_flags)
+#endif
+{
+ struct ttm_tt *tt;
+
+ tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+ if (!tt)
+ return NULL;
+
+#if RTLNX_VER_MAX(5,10,0) && !RTLNX_RHEL_RANGE(8,5, 8,99)
+ tt->func = &vbox_tt_backend_func;
+#endif
+#if RTLNX_VER_MIN(5,19,0) || RTLNX_RHEL_RANGE(8,8, 8,99) || RTLNX_RHEL_RANGE(9,2, 9,99) || RTLNX_SUSE_MAJ_PREREQ(15,5)
+ if (ttm_tt_init(tt, bo, page_flags, ttm_write_combined, 0)) {
+#elif RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ if (ttm_tt_init(tt, bo, page_flags, ttm_write_combined)) {
+#elif RTLNX_VER_MIN(4,17,0) || RTLNX_RHEL_MAJ_PREREQ(7,6) || RTLNX_SUSE_MAJ_PREREQ(15,1) || RTLNX_SUSE_MAJ_PREREQ(12,5)
+ if (ttm_tt_init(tt, bo, page_flags)) {
+#else
+ if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) {
+#endif
+
+ kfree(tt);
+ return NULL;
+ }
+
+ return tt;
+}
+
+#if RTLNX_VER_MAX(4,17,0)
+# if RTLNX_VER_MAX(4,16,0) && !RTLNX_RHEL_MAJ_PREREQ(7,6) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+static int vbox_ttm_tt_populate(struct ttm_tt *ttm)
+{
+ return ttm_pool_populate(ttm);
+}
+# else
+static int vbox_ttm_tt_populate(struct ttm_tt *ttm,
+ struct ttm_operation_ctx *ctx)
+{
+ return ttm_pool_populate(ttm, ctx);
+}
+# endif
+
+static void vbox_ttm_tt_unpopulate(struct ttm_tt *ttm)
+{
+ ttm_pool_unpopulate(ttm);
+}
+#endif
+
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+static int vbox_bo_move(struct ttm_buffer_object *bo, bool evict,
+ struct ttm_operation_ctx *ctx, struct ttm_resource *new_mem,
+ struct ttm_place *hop)
+{
+# if RTLNX_VER_MIN(6,4,0)
+ if (!bo->resource)
+ {
+ if (new_mem->mem_type != TTM_PL_SYSTEM)
+ {
+ hop->mem_type = TTM_PL_SYSTEM;
+ hop->flags = TTM_PL_FLAG_TEMPORARY;
+ return -EMULTIHOP;
+ }
+ ttm_bo_move_null(bo, new_mem);
+ return 0;
+ }
+# endif
+ return ttm_bo_move_memcpy(bo, ctx, new_mem);
+}
+#endif
+
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+static struct ttm_device_funcs vbox_bo_driver = {
+#else /* < 5.13.0 */
+static struct ttm_bo_driver vbox_bo_driver = {
+#endif /* < 5.13.0 */
+ .ttm_tt_create = vbox_ttm_tt_create,
+#if RTLNX_VER_MIN(5,10,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ .ttm_tt_destroy = vbox_ttm_tt_destroy,
+#endif
+#if RTLNX_VER_MAX(4,17,0)
+ .ttm_tt_populate = vbox_ttm_tt_populate,
+ .ttm_tt_unpopulate = vbox_ttm_tt_unpopulate,
+#endif
+#if RTLNX_VER_MAX(5,10,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ .init_mem_type = vbox_bo_init_mem_type,
+#endif
+#if RTLNX_VER_MIN(4,10,0) || RTLNX_RHEL_MAJ_PREREQ(7,4)
+ .eviction_valuable = ttm_bo_eviction_valuable,
+#endif
+ .evict_flags = vbox_bo_evict_flags,
+#if RTLNX_VER_MAX(5,14,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
+ .verify_access = vbox_bo_verify_access,
+#endif
+ .io_mem_reserve = &vbox_ttm_io_mem_reserve,
+ .io_mem_free = &vbox_ttm_io_mem_free,
+#if RTLNX_VER_MIN(4,12,0) || RTLNX_RHEL_MAJ_PREREQ(7,5)
+# if RTLNX_VER_MAX(4,16,0) && !RTLNX_RHEL_MAJ_PREREQ(7,6) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+ .io_mem_pfn = ttm_bo_default_io_mem_pfn,
+# endif
+#endif
+#if (RTLNX_VER_RANGE(4,7,0, 4,11,0) || RTLNX_RHEL_MAJ_PREREQ(7,4)) && !RTLNX_RHEL_MAJ_PREREQ(7,5)
+ .lru_tail = &ttm_bo_default_lru_tail,
+ .swap_lru_tail = &ttm_bo_default_swap_lru_tail,
+#endif
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ .move = &vbox_bo_move,
+#endif
+};
+
+int vbox_mm_init(struct vbox_private *vbox)
+{
+ int ret;
+ struct drm_device *dev = vbox->dev;
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ struct ttm_device *bdev = &vbox->ttm.bdev;
+#else
+ struct ttm_bo_device *bdev = &vbox->ttm.bdev;
+#endif
+
+#if RTLNX_VER_MAX(5,0,0) && !RTLNX_RHEL_MAJ_PREREQ(7,7) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
+ ret = vbox_ttm_global_init(vbox);
+ if (ret)
+ return ret;
+#endif
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ ret = ttm_device_init(&vbox->ttm.bdev,
+#else
+ ret = ttm_bo_device_init(&vbox->ttm.bdev,
+#endif
+#if RTLNX_VER_MAX(5,0,0) && !RTLNX_RHEL_MAJ_PREREQ(7,7) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
+ vbox->ttm.bo_global_ref.ref.object,
+#endif
+ &vbox_bo_driver,
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ dev->dev,
+#endif
+#if RTLNX_VER_MIN(3,15,0) || RTLNX_RHEL_MAJ_PREREQ(7,1)
+ dev->anon_inode->i_mapping,
+#endif
+#if RTLNX_VER_MIN(5,5,0) || RTLNX_RHEL_MIN(8,3) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ dev->vma_offset_manager,
+#elif RTLNX_VER_MAX(5,2,0) && !RTLNX_RHEL_MAJ_PREREQ(8,2)
+ DRM_FILE_PAGE_OFFSET,
+#endif
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ false,
+#endif
+ true);
+ if (ret) {
+ DRM_ERROR("Error initialising bo driver; %d\n", ret);
+#if RTLNX_VER_MAX(5,0,0) && !RTLNX_RHEL_MAJ_PREREQ(7,7) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
+ goto err_ttm_global_release;
+#else
+ return ret;
+#endif
+ }
+
+#if RTLNX_VER_MIN(5,10,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ ret = ttm_range_man_init(bdev, TTM_PL_VRAM, false,
+ vbox->available_vram_size >> PAGE_SHIFT);
+#else
+ ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
+ vbox->available_vram_size >> PAGE_SHIFT);
+#endif
+ if (ret) {
+ DRM_ERROR("Failed ttm VRAM init: %d\n", ret);
+ goto err_device_release;
+ }
+
+#ifdef DRM_MTRR_WC
+ vbox->fb_mtrr = drm_mtrr_add(pci_resource_start(VBOX_DRM_TO_PCI_DEV(dev), 0),
+ pci_resource_len(VBOX_DRM_TO_PCI_DEV(dev), 0),
+ DRM_MTRR_WC);
+#else
+ vbox->fb_mtrr = arch_phys_wc_add(pci_resource_start(VBOX_DRM_TO_PCI_DEV(dev), 0),
+ pci_resource_len(VBOX_DRM_TO_PCI_DEV(dev), 0));
+#endif
+ return 0;
+
+err_device_release:
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ ttm_device_fini(&vbox->ttm.bdev);
+#else
+ ttm_bo_device_release(&vbox->ttm.bdev);
+#endif
+#if RTLNX_VER_MAX(5,0,0) && !RTLNX_RHEL_MAJ_PREREQ(7,7) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
+err_ttm_global_release:
+ vbox_ttm_global_release(vbox);
+#endif
+ return ret;
+}
+
+void vbox_mm_fini(struct vbox_private *vbox)
+{
+#ifdef DRM_MTRR_WC
+ drm_mtrr_del(vbox->fb_mtrr,
+ pci_resource_start(VBOX_DRM_TO_PCI_DEV(vbox->dev), 0),
+ pci_resource_len(VBOX_DRM_TO_PCI_DEV(vbox->dev), 0), DRM_MTRR_WC);
+#else
+ arch_phys_wc_del(vbox->fb_mtrr);
+#endif
+#if RTLNX_VER_MIN(5,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ ttm_device_fini(&vbox->ttm.bdev);
+#else
+ ttm_bo_device_release(&vbox->ttm.bdev);
+#endif
+#if RTLNX_VER_MAX(5,0,0) && !RTLNX_RHEL_MAJ_PREREQ(7,7) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
+ vbox_ttm_global_release(vbox);
+#endif
+}
+
+void vbox_ttm_placement(struct vbox_bo *bo, u32 mem_type)
+{
+ u32 c = 0;
+#if RTLNX_VER_MAX(3,18,0) && !RTLNX_RHEL_MAJ_PREREQ(7,2)
+ bo->placement.fpfn = 0;
+ bo->placement.lpfn = 0;
+#else
+ unsigned int i;
+#endif
+
+ bo->placement.placement = bo->placements;
+ bo->placement.busy_placement = bo->placements;
+
+ if (mem_type & VBOX_MEM_TYPE_VRAM) {
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ bo->placements[c].mem_type = TTM_PL_VRAM;
+ PLACEMENT_FLAGS(bo->placements[c++]) = 0;
+#elif RTLNX_VER_MIN(5,10,0)
+ bo->placements[c].mem_type = TTM_PL_VRAM;
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED;
+#else
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
+#endif
+ }
+ if (mem_type & VBOX_MEM_TYPE_SYSTEM) {
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ bo->placements[c].mem_type = TTM_PL_SYSTEM;
+ PLACEMENT_FLAGS(bo->placements[c++]) = 0;
+#elif RTLNX_VER_MIN(5,10,0)
+ bo->placements[c].mem_type = TTM_PL_SYSTEM;
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_MASK_CACHING;
+#else
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+#endif
+ }
+ if (!c) {
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ bo->placements[c].mem_type = TTM_PL_SYSTEM;
+ PLACEMENT_FLAGS(bo->placements[c++]) = 0;
+#elif RTLNX_VER_MIN(5,10,0)
+ bo->placements[c].mem_type = TTM_PL_SYSTEM;
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_MASK_CACHING;
+#else
+ PLACEMENT_FLAGS(bo->placements[c++]) =
+ TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+#endif
+ }
+
+ bo->placement.num_placement = c;
+ bo->placement.num_busy_placement = c;
+
+#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
+ for (i = 0; i < c; ++i) {
+ bo->placements[i].fpfn = 0;
+ bo->placements[i].lpfn = 0;
+ }
+#endif
+}
+
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+static const struct drm_gem_object_funcs vbox_drm_gem_object_funcs = {
+ .free = vbox_gem_free_object,
+ .print_info = drm_gem_ttm_print_info,
+# if RTLNX_VER_MIN(6,5,0)
+ .vmap = drm_gem_ttm_vmap,
+ .vunmap = drm_gem_ttm_vunmap,
+# endif
+# if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ .mmap = drm_gem_ttm_mmap,
+# endif
+};
+#endif
+
+int vbox_bo_create(struct drm_device *dev, int size, int align,
+ u32 flags, struct vbox_bo **pvboxbo)
+{
+ struct vbox_private *vbox = dev->dev_private;
+ struct vbox_bo *vboxbo;
+#if RTLNX_VER_MAX(5,13,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
+ size_t acc_size;
+#endif
+ int ret;
+
+ vboxbo = kzalloc(sizeof(*vboxbo), GFP_KERNEL);
+ if (!vboxbo)
+ return -ENOMEM;
+
+ ret = drm_gem_object_init(dev, &vboxbo->gem, size);
+ if (ret)
+ goto err_free_vboxbo;
+
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ if (!vboxbo->gem.funcs) {
+ vboxbo->gem.funcs = &vbox_drm_gem_object_funcs;
+ }
+#endif
+ vboxbo->bo.bdev = &vbox->ttm.bdev;
+#if RTLNX_VER_MAX(3,15,0) && !RTLNX_RHEL_MAJ_PREREQ(7,1)
+ vboxbo->bo.bdev->dev_mapping = dev->dev_mapping;
+#endif
+
+ vbox_ttm_placement(vboxbo, VBOX_MEM_TYPE_VRAM | VBOX_MEM_TYPE_SYSTEM);
+
+#if RTLNX_VER_MAX(5,13,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
+ acc_size = ttm_bo_dma_acc_size(&vbox->ttm.bdev, size,
+ sizeof(struct vbox_bo));
+#endif
+
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ /* Initialization of the following was removed from DRM stack
+ * in 5.14, so we need to do it manually. */
+ vboxbo->bo.base.funcs = &vbox_drm_gem_object_funcs;
+ kref_init(&vboxbo->bo.base.refcount);
+ vboxbo->bo.base.size = size;
+ vboxbo->bo.base.dev = dev;
+ dma_resv_init(&vboxbo->bo.base._resv);
+ drm_vma_node_reset(&vboxbo->bo.base.vma_node);
+#endif
+
+#if RTLNX_VER_MIN(6,1,0) || RTLNX_RHEL_RANGE(8,9, 8,99) || RTLNX_RHEL_RANGE(9,3, 9,99) || RTLNX_SUSE_MAJ_PREREQ(15,5)
+ ret = ttm_bo_init_validate(&vbox->ttm.bdev, &vboxbo->bo,
+#else
+ ret = ttm_bo_init(&vbox->ttm.bdev, &vboxbo->bo, size,
+#endif /* < 6.1.0 */
+ ttm_bo_type_device, &vboxbo->placement,
+#if RTLNX_VER_MAX(4,17,0) && !RTLNX_RHEL_MAJ_PREREQ(7,6) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+ align >> PAGE_SHIFT, false, NULL, acc_size,
+#elif RTLNX_VER_MAX(5,13,0) && !RTLNX_RHEL_RANGE(8,6, 8,99) /* < 5.13.0, < RHEL(8.6, 8.99) */
+ align >> PAGE_SHIFT, false, acc_size,
+#else /* > 5.13.0 */
+ align >> PAGE_SHIFT, false,
+#endif /* > 5.13.0 */
+#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
+ NULL, NULL, vbox_bo_ttm_destroy);
+#else
+ NULL, vbox_bo_ttm_destroy);
+#endif
+ if (ret)
+ {
+ /* In case of failure, ttm_bo_init() supposed to call
+ * vbox_bo_ttm_destroy() which in turn will free @vboxbo. */
+ goto err_exit;
+ }
+
+ *pvboxbo = vboxbo;
+
+ return 0;
+
+err_free_vboxbo:
+ kfree(vboxbo);
+err_exit:
+ return ret;
+}
+
+static inline u64 vbox_bo_gpu_offset(struct vbox_bo *bo)
+{
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ return bo->bo.resource->start << PAGE_SHIFT;
+#elif RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
+ return bo->bo.mem.start << PAGE_SHIFT;
+#else
+ return bo->bo.offset;
+#endif
+}
+
+int vbox_bo_pin(struct vbox_bo *bo, u32 mem_type, u64 *gpu_addr)
+{
+#if RTLNX_VER_MIN(4,16,0) || RTLNX_RHEL_MAJ_PREREQ(7,6) || RTLNX_SUSE_MAJ_PREREQ(15,1) || RTLNX_SUSE_MAJ_PREREQ(12,5)
+ struct ttm_operation_ctx ctx = { false, false };
+#endif
+ int ret;
+#if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ int i;
+#endif
+
+ if (bo->pin_count) {
+ bo->pin_count++;
+ if (gpu_addr)
+ *gpu_addr = vbox_bo_gpu_offset(bo);
+
+ return 0;
+ }
+
+ vbox_ttm_placement(bo, mem_type);
+
+#if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ for (i = 0; i < bo->placement.num_placement; i++)
+ PLACEMENT_FLAGS(bo->placements[i]) |= TTM_PL_FLAG_NO_EVICT;
+#endif
+
+#if RTLNX_VER_MAX(4,16,0) && !RTLNX_RHEL_MAJ_PREREQ(7,6) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+#else
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
+#endif
+ if (ret)
+ return ret;
+
+ bo->pin_count = 1;
+
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ ttm_bo_pin(&bo->bo);
+#endif
+
+ if (gpu_addr)
+ *gpu_addr = vbox_bo_gpu_offset(bo);
+
+ return 0;
+}
+
+int vbox_bo_unpin(struct vbox_bo *bo)
+{
+#if RTLNX_VER_MIN(4,16,0) || RTLNX_RHEL_MAJ_PREREQ(7,6) || RTLNX_SUSE_MAJ_PREREQ(15,1) || RTLNX_SUSE_MAJ_PREREQ(12,5)
+# if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ struct ttm_operation_ctx ctx = { false, false };
+# endif
+#endif
+ int ret = 0;
+#if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ int i;
+#endif
+
+ if (!bo->pin_count) {
+ DRM_ERROR("unpin bad %p\n", bo);
+ return 0;
+ }
+ bo->pin_count--;
+ if (bo->pin_count)
+ return 0;
+
+#if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ for (i = 0; i < bo->placement.num_placement; i++)
+ PLACEMENT_FLAGS(bo->placements[i]) &= ~TTM_PL_FLAG_NO_EVICT;
+#endif
+
+#if RTLNX_VER_MAX(4,16,0) && !RTLNX_RHEL_MAJ_PREREQ(7,6) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+#elif RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
+#endif
+ if (ret)
+ return ret;
+
+#if RTLNX_VER_MIN(5,11,0) || RTLNX_RHEL_RANGE(8,5, 8,99)
+ ttm_bo_unpin(&bo->bo);
+#endif
+
+ return 0;
+}
+
+#if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
+/*
+ * Move a vbox-owned buffer object to system memory if no one else has it
+ * pinned. The caller must have pinned it previously, and this call will
+ * release the caller's pin.
+ */
+int vbox_bo_push_sysram(struct vbox_bo *bo)
+{
+# if RTLNX_VER_MIN(4,16,0) || RTLNX_RHEL_MAJ_PREREQ(7,6) || RTLNX_SUSE_MAJ_PREREQ(15,1) || RTLNX_SUSE_MAJ_PREREQ(12,5)
+ struct ttm_operation_ctx ctx = { false, false };
+# endif
+ int i, ret;
+
+ if (!bo->pin_count) {
+ DRM_ERROR("unpin bad %p\n", bo);
+ return 0;
+ }
+ bo->pin_count--;
+ if (bo->pin_count)
+ return 0;
+
+ if (bo->kmap.virtual)
+ ttm_bo_kunmap(&bo->kmap);
+
+ vbox_ttm_placement(bo, VBOX_MEM_TYPE_SYSTEM);
+
+ for (i = 0; i < bo->placement.num_placement; i++)
+ PLACEMENT_FLAGS(bo->placements[i]) |= TTM_PL_FLAG_NO_EVICT;
+
+# if RTLNX_VER_MAX(4,16,0) && !RTLNX_RHEL_MAJ_PREREQ(7,6) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+# else
+ ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
+# endif
+ if (ret) {
+ DRM_ERROR("pushing to VRAM failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+int vbox_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *file_priv;
+ struct vbox_private *vbox;
+ int ret = -EINVAL;
+
+ if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
+ return -EINVAL;
+
+ file_priv = filp->private_data;
+ vbox = file_priv->minor->dev->dev_private;
+
+#if RTLNX_VER_MIN(5,14,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
+ (void)vbox;
+ if (drm_dev_is_unplugged(file_priv->minor->dev))
+ return -ENODEV;
+ ret = drm_gem_mmap(filp, vma);
+#else
+ ret = ttm_bo_mmap(filp, vma, &vbox->ttm.bdev);
+#endif
+ return ret;
+}