diff --git a/README.md b/README.md index 8e1224a3..ae2a1dbf 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ After having all the prerequisites installed, the build process can be condensed to the following steps: ```shell -$ # Download the VirtualBox 7.0.20 source package from Oracle. -$ tar xf VirtualBox-7.0.20.tar.bz2 +$ # Download the VirtualBox 7.1.4 source package from Oracle. +$ tar xf VirtualBox-7.1.4.tar.bz2 $ git clone https://github.com/cyberus-technology/virtualbox-kvm vbox-kvm -$ cd VirtualBox-7.0.20 +$ cd VirtualBox-7.1.4 $ git init $ git add * $ git commit -m "VirtualBox vanilla code" diff --git a/patches/0001-NEM-Implement-KVM-backend.patch b/patches/0001-NEM-Implement-KVM-backend.patch index 8763aed0..a5c2b412 100644 --- a/patches/0001-NEM-Implement-KVM-backend.patch +++ b/patches/0001-NEM-Implement-KVM-backend.patch @@ -305,40 +305,43 @@ NEM/KVM: fix hardened builds with KVM include/iprt/thread.h | 6 + include/iprt/x86.h | 8 + src/VBox/Devices/PC/DevACPI.cpp | 71 +- - src/VBox/Devices/PC/DevIoApic.cpp | 185 +- + src/VBox/Devices/PC/DevIoApic.cpp | 185 ++- src/VBox/Devices/PC/DevPIC.cpp | 65 + src/VBox/HostDrivers/Support/Makefile.kmk | 1 + .../Support/linux/SUPLib-linux.cpp | 9 + src/VBox/Main/Makefile.kmk | 4 +- + src/VBox/Main/src-server/HostImpl.cpp | 2 + + src/VBox/Runtime/Makefile.kmk | 4 +- src/VBox/Runtime/r3/posix/thread-posix.cpp | 4 + src/VBox/Runtime/testcase/Makefile.kmk | 1 + src/VBox/VMM/Makefile.kmk | 6 +- src/VBox/VMM/VMMAll/APICAll.cpp | 10 + - src/VBox/VMM/VMMAll/GIMAllHvOnKvm.cpp | 134 ++ + src/VBox/VMM/VMMAll/GIMAllHvOnKvm.cpp | 136 ++ src/VBox/VMM/VMMAll/PGMAllBth.h | 3 + src/VBox/VMM/VMMAll/TMAll.cpp | 5 +- src/VBox/VMM/VMMAll/TMAllVirtual.cpp | 4 + src/VBox/VMM/VMMR3/APIC.cpp | 21 + src/VBox/VMM/VMMR3/CPUM.cpp | 15 + - src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp | 37 +- + src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp | 28 +- src/VBox/VMM/VMMR3/EM.cpp | 11 + src/VBox/VMM/VMMR3/GIMHv.cpp | 77 +- - src/VBox/VMM/VMMR3/GIMHvOnKvm.cpp | 640 +++++++ - src/VBox/VMM/VMMR3/NEMR3Native-linux.cpp | 1605 ++++++++++++++--- + src/VBox/VMM/VMMR3/GIMHvOnKvm.cpp | 640 ++++++++ + src/VBox/VMM/VMMR3/NEMR3Native-linux.cpp | 1372 +++++++++++++++-- + .../VMM/VMMR3/NEMR3NativeTemplate-linux.cpp.h | 116 ++ src/VBox/VMM/VMMR3/PDMDevMiscHlp.cpp | 102 ++ src/VBox/VMM/VMMR3/PGMPhys.cpp | 5 + src/VBox/VMM/VMMR3/VMM.cpp | 5 + - src/VBox/VMM/include/GIMHvInternal.h | 3 + + src/VBox/VMM/include/GIMHvInternal.h | 11 + src/VBox/VMM/include/NEMInternal.h | 34 +- - 33 files changed, 3125 insertions(+), 249 deletions(-) + 36 files changed, 3089 insertions(+), 175 deletions(-) create mode 100644 vboxsrc/src/VBox/VMM/VMMAll/GIMAllHvOnKvm.cpp create mode 100644 vboxsrc/src/VBox/VMM/VMMR3/GIMHvOnKvm.cpp diff --git a/Config.kmk b/Config.kmk -index 2bc510416a..8812da09c5 100644 +index bdcbd3987c..6ac1d9baee 100644 --- a/Config.kmk +++ b/Config.kmk -@@ -1587,6 +1587,19 @@ ifdef VBOX_HEADLESS +@@ -1677,6 +1677,19 @@ ifdef VBOX_HEADLESS VBOX_WITH_VRDP_RDESKTOP = endif @@ -358,9 +361,9 @@ index 2bc510416a..8812da09c5 100644 # # Undefined VBOX_WITH_MAIN implies exclusion of a few more items. # -@@ -1996,6 +2009,14 @@ endif - ifdef VBOX_WITH_DRIVERLESS_FORCED - DEFS += VBOX_WITH_DRIVERLESS_FORCED +@@ -2143,6 +2156,14 @@ endif + ifdef VBOX_WITH_STATIC_ARM64_PAGE_SHIFT + DEFS.linux.arm64 += IPRT_STATIC_ARM64_PAGE_SHIFT=$(VBOX_WITH_STATIC_ARM64_PAGE_SHIFT) endif +CYBERUS_CXX_FLAGS = -Werror -Wall +ifdef VBOX_WITH_KVM @@ -373,17 +376,17 @@ index 2bc510416a..8812da09c5 100644 # Don't flood CDEFS, old MASMs doesn't like too many defines. ifdef VBOX_WITH_DEBUGGER -@@ -3522,6 +3543,8 @@ ifndef VBOX_GCC_std +@@ -3683,6 +3704,8 @@ ifndef VBOX_GCC_std VBOX_GCC_std := -std=c++17 # else if "$(VBOX_CLANG_VERSION_CXX)" vge 60000 # Most language features complete by v6. Lib stuff was less complete in v6, but hopefully acceptable for out purposes. #VBOX_GCC_std := -std=c++17 -+ else if VBOX_WITH_KVM ++ else if "$(VBOX_WITH_KVM)" veq 1 + VBOX_GCC_std := -std=c++17 else if "$(VBOX_CLANG_VERSION_CXX)" vge 50000 # darwin Xcode 5 allegedly knows what C++11 is VBOX_GCC_std := -std=c++11 # else if "$(VBOX_GCC_VERSION_CXX)" vge 70000 # Language feature P0512R0 was v8, rest v7 or earlier. Most lib stuff present in 7, complete in v12. diff --git a/configure b/configure -index 4b69712cd6..22709bf934 100755 +index 65791d5540..c3f4581b85 100755 --- a/configure +++ b/configure @@ -86,6 +86,7 @@ SETUP_WINE= @@ -394,7 +397,7 @@ index 4b69712cd6..22709bf934 100755 WITH_XPCOM=1 WITH_PYTHON=1 WITH_JAVA=1 -@@ -2529,6 +2530,7 @@ cat << EOF +@@ -2543,6 +2544,7 @@ cat << EOF --build-libssl build openssl from sources --build-libtpms build libtpms from sources --build-liblzma build liblzma from sources @@ -402,7 +405,7 @@ index 4b69712cd6..22709bf934 100755 EOF [ $OSE -eq 0 ] && cat << EOF --build-libcurl build libcurl from sources -@@ -2688,6 +2690,9 @@ for option in "$@"; do +@@ -2702,6 +2704,9 @@ for option in "$@"; do --with-linux=*) LINUX=`echo $option | cut -d'=' -f2` ;; @@ -412,7 +415,7 @@ index 4b69712cd6..22709bf934 100755 --with-makeself=*) MAKESELF=`echo $option | cut -d'=' -f2` ;; -@@ -2969,6 +2974,7 @@ fi +@@ -2983,6 +2988,7 @@ fi [ $WITH_JAVA -eq 0 ] && cnf_append "VBOX_WITH_JWS" "" [ $WITH_HARDENING -eq 0 ] && cnf_append "VBOX_WITHOUT_HARDENING" "1" [ $WITH_HARDENING -eq 2 ] && cnf_append "VBOX_WITH_HARDENING" "2" @@ -421,7 +424,7 @@ index 4b69712cd6..22709bf934 100755 [ $WITH_LIBTPMS -eq 0 ] && cnf_append "VBOX_WITH_LIBTPMS" "" [ $WITH_LIBLZMA -eq 0 ] && cnf_append "VBOX_WITH_LIBLZMA" "" diff --git a/include/VBox/vmm/nem.h b/include/VBox/vmm/nem.h -index 0d960a7eee..8603297854 100644 +index 28b4079b9c..3630cd0283 100644 --- a/include/VBox/vmm/nem.h +++ b/include/VBox/vmm/nem.h @@ -43,6 +43,14 @@ @@ -591,7 +594,7 @@ index 0d960a7eee..8603297854 100644 * @{ */ /** Set if the range is replacing RAM rather that unused space. */ diff --git a/include/VBox/vmm/pdmdev.h b/include/VBox/vmm/pdmdev.h -index f895eb86f5..525d82e030 100644 +index e602c5fdeb..ba6f001b68 100644 --- a/include/VBox/vmm/pdmdev.h +++ b/include/VBox/vmm/pdmdev.h @@ -64,6 +64,49 @@ @@ -644,7 +647,7 @@ index f895eb86f5..525d82e030 100644 RT_C_DECLS_BEGIN -@@ -1747,6 +1790,35 @@ typedef struct PDMPICHLP +@@ -1749,6 +1792,35 @@ typedef struct PDMPICHLP */ DECLCALLBACKMEMBER(void, pfnUnlock,(PPDMDEVINS pDevIns)); @@ -680,7 +683,7 @@ index f895eb86f5..525d82e030 100644 /** Just a safety precaution. */ uint32_t u32TheEnd; } PDMPICHLP; -@@ -1974,6 +2046,55 @@ typedef struct PDMIOAPICHLP +@@ -1976,6 +2048,55 @@ typedef struct PDMIOAPICHLP */ DECLCALLBACKMEMBER(int, pfnIommuMsiRemap,(PPDMDEVINS pDevIns, uint16_t idDevice, PCMSIMSG pMsiIn, PMSIMSG pMsiOut)); @@ -737,10 +740,10 @@ index f895eb86f5..525d82e030 100644 uint32_t u32TheEnd; } PDMIOAPICHLP; diff --git a/include/iprt/mangling.h b/include/iprt/mangling.h -index 0c8c027f1b..7b27bba35d 100644 +index 4527d6cec9..7baf8399a0 100644 --- a/include/iprt/mangling.h +++ b/include/iprt/mangling.h -@@ -2555,6 +2555,7 @@ +@@ -2617,6 +2617,7 @@ # define RTThreadIsSelfKnown RT_MANGLER(RTThreadIsSelfKnown) # define RTThreadNativeSelf RT_MANGLER(RTThreadNativeSelf) # define RTThreadControlPokeSignal RT_MANGLER(RTThreadControlPokeSignal) /* not-win not-os2 */ @@ -749,7 +752,7 @@ index 0c8c027f1b..7b27bba35d 100644 # define RTThreadPreemptDisable RT_MANGLER(RTThreadPreemptDisable) /* r0drv */ # define RTThreadPreemptIsEnabled RT_MANGLER(RTThreadPreemptIsEnabled) /* r0drv */ diff --git a/include/iprt/thread.h b/include/iprt/thread.h -index 7d9257ec4c..243d76de75 100644 +index 9a3d42fdf3..3540374de3 100644 --- a/include/iprt/thread.h +++ b/include/iprt/thread.h @@ -555,6 +555,12 @@ RTDECL(int) RTThreadPoke(RTTHREAD hThread); @@ -766,10 +769,10 @@ index 7d9257ec4c..243d76de75 100644 # ifdef IN_RING0 diff --git a/include/iprt/x86.h b/include/iprt/x86.h -index 95ab7b439a..96c7d1b019 100644 +index 31d73ace18..54a411b925 100644 --- a/include/iprt/x86.h +++ b/include/iprt/x86.h -@@ -667,6 +667,8 @@ typedef const X86CPUIDFEATEDX *PCX86CPUIDFEATEDX; +@@ -682,6 +682,8 @@ typedef const X86CPUIDFEATEDX *PCX86CPUIDFEATEDX; #define X86_CPUID_STEXT_FEATURE_EBX_SMAP RT_BIT_32(20) /** EBX Bit 23 - CLFLUSHOPT - Supports CLFLUSHOPT (Cache Line Flush). */ #define X86_CPUID_STEXT_FEATURE_EBX_CLFLUSHOPT RT_BIT_32(23) @@ -778,16 +781,16 @@ index 95ab7b439a..96c7d1b019 100644 /** EBX Bit 25 - INTEL_PT - Supports Intel Processor Trace. */ #define X86_CPUID_STEXT_FEATURE_EBX_INTEL_PT RT_BIT_32(25) /** EBX Bit 26 - AVX512PF - Supports AVX512PF. */ -@@ -686,6 +688,8 @@ typedef const X86CPUIDFEATEDX *PCX86CPUIDFEATEDX; - #define X86_CPUID_STEXT_FEATURE_ECX_PKU RT_BIT_32(3) - /** ECX Bit 4 - OSPKE - Protection keys for user mode pages enabled. */ +@@ -703,6 +705,8 @@ typedef const X86CPUIDFEATEDX *PCX86CPUIDFEATEDX; #define X86_CPUID_STEXT_FEATURE_ECX_OSPKE RT_BIT_32(4) + /** ECX Bit 7 - CET_SS - Supports CET shadow stack features. */ + #define X86_CPUID_STEXT_FEATURE_ECX_CET_SS RT_BIT_32(7) +/** ECX Bit 8 - GFNI - Supports Galois Field instructions . */ +#define X86_CPUID_STEXT_FEATURE_ECX_GFNI RT_BIT_32(8) /** ECX Bits 17-21 - MAWAU - Value used by BNDLDX and BNDSTX. */ #define X86_CPUID_STEXT_FEATURE_ECX_MAWAU UINT32_C(0x003e0000) /** ECX Bit 22 - RDPID - Support pread process ID. */ -@@ -693,8 +697,12 @@ typedef const X86CPUIDFEATEDX *PCX86CPUIDFEATEDX; +@@ -710,8 +714,12 @@ typedef const X86CPUIDFEATEDX *PCX86CPUIDFEATEDX; /** ECX Bit 30 - SGX_LC - Supports SGX launch configuration. */ #define X86_CPUID_STEXT_FEATURE_ECX_SGX_LC RT_BIT_32(30) @@ -797,14 +800,14 @@ index 95ab7b439a..96c7d1b019 100644 #define X86_CPUID_STEXT_FEATURE_EDX_MD_CLEAR RT_BIT_32(10) +/** EDX Bit 14 - SERIALIZE - Supports the SERIALIZE CPU instruction. */ +#define X86_CPUID_STEXT_FEATURE_EDX_SERIALIZE RT_BIT_32(14) + /** EDX Bit 20 - CET_IBT - Supports CET indirect branch tracking features. */ + #define X86_CPUID_STEXT_FEATURE_EDX_CET_IBT RT_BIT_32(20) /** EDX Bit 26 - IBRS & IBPB - Supports the IBRS flag in IA32_SPEC_CTRL and - * IBPB command in IA32_PRED_CMD. */ - #define X86_CPUID_STEXT_FEATURE_EDX_IBRS_IBPB RT_BIT_32(26) diff --git a/src/VBox/Devices/PC/DevACPI.cpp b/src/VBox/Devices/PC/DevACPI.cpp -index abf2ba5e25..672b9afae0 100644 +index 18cb6dc731..570facf156 100644 --- a/src/VBox/Devices/PC/DevACPI.cpp +++ b/src/VBox/Devices/PC/DevACPI.cpp -@@ -812,7 +812,11 @@ struct ACPITBLISO +@@ -814,7 +814,11 @@ struct ACPITBLISO uint16_t u16Flags; /**< MPS INTI flags Global */ }; AssertCompileSize(ACPITBLISO, 10); @@ -817,7 +820,7 @@ index abf2ba5e25..672b9afae0 100644 /** HPET Descriptor Structure */ struct ACPITBLHPET -@@ -3295,8 +3299,73 @@ static void acpiR3SetupMadt(PPDMDEVINS pDevIns, PACPISTATE pThis, RTGCPHYS32 add +@@ -3318,8 +3322,73 @@ static void acpiR3SetupMadt(PPDMDEVINS pDevIns, PACPISTATE pThis, RTGCPHYS32 add isos[1].u8Bus = 0; /* Must be 0 */ isos[1].u8Source = 9; /* IRQ9 */ isos[1].u32GSI = 9; /* connected to pin 9 */ @@ -892,7 +895,7 @@ index abf2ba5e25..672b9afae0 100644 madt.header_addr()->u8Checksum = acpiR3Checksum(madt.data(), madt.size()); acpiR3PhysCopy(pDevIns, addr, madt.data(), madt.size()); diff --git a/src/VBox/Devices/PC/DevIoApic.cpp b/src/VBox/Devices/PC/DevIoApic.cpp -index a69d8e3f02..f56c66725d 100644 +index 508410bc29..abdf8c6f29 100644 --- a/src/VBox/Devices/PC/DevIoApic.cpp +++ b/src/VBox/Devices/PC/DevIoApic.cpp @@ -32,6 +32,14 @@ @@ -1225,7 +1228,7 @@ index a69d8e3f02..f56c66725d 100644 * Validate and read the configuration. */ diff --git a/src/VBox/Devices/PC/DevPIC.cpp b/src/VBox/Devices/PC/DevPIC.cpp -index b4e195952e..f44e2c6f73 100644 +index 495322dc04..7358b5a965 100644 --- a/src/VBox/Devices/PC/DevPIC.cpp +++ b/src/VBox/Devices/PC/DevPIC.cpp @@ -366,6 +366,16 @@ static DECLCALLBACK(void) picSetIrq(PPDMDEVINS pDevIns, int iIrq, int iLevel, ui @@ -1322,10 +1325,10 @@ index b4e195952e..f44e2c6f73 100644 return VINF_SUCCESS; } diff --git a/src/VBox/HostDrivers/Support/Makefile.kmk b/src/VBox/HostDrivers/Support/Makefile.kmk -index 46c2784cc4..9f9ad3c9c7 100644 +index b3902ab751..1f5b64414b 100644 --- a/src/VBox/HostDrivers/Support/Makefile.kmk +++ b/src/VBox/HostDrivers/Support/Makefile.kmk -@@ -191,6 +191,7 @@ SUPR3_DEFS = \ +@@ -195,6 +195,7 @@ SUPR3_DEFS = \ $(if $(VBOX_WITH_MAIN),VBOX_WITH_MAIN,) \ $(if $(VBOX_WITH_RAW_MODE),VBOX_WITH_RAW_MODE,) \ $(if $(VBOX_WITH_DRIVERLESS_NEM_FALLBACK),VBOX_WITH_DRIVERLESS_NEM_FALLBACK,) \ @@ -1334,7 +1337,7 @@ index 46c2784cc4..9f9ad3c9c7 100644 VBOX_PERMIT_EVEN_MORE SUPR3_INCS := $(PATH_SUB_CURRENT) diff --git a/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp b/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp -index 5bdcda63c4..c01a10cf59 100644 +index 16cea2fb52..928710fa42 100644 --- a/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp +++ b/src/VBox/HostDrivers/Support/linux/SUPLib-linux.cpp @@ -96,6 +96,11 @@ DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags @@ -1361,10 +1364,10 @@ index 5bdcda63c4..c01a10cf59 100644 if ( !pThis->fSysMadviseWorks && (fFlags & (SUP_PAGE_ALLOC_F_FOR_LOCKING | SUP_PAGE_ALLOC_F_LARGE_PAGES)) == SUP_PAGE_ALLOC_F_FOR_LOCKING) diff --git a/src/VBox/Main/Makefile.kmk b/src/VBox/Main/Makefile.kmk -index 00bab609bf..d98ad1ff8d 100644 +index fd74244f47..bc3bfbb771 100644 --- a/src/VBox/Main/Makefile.kmk +++ b/src/VBox/Main/Makefile.kmk -@@ -1092,7 +1092,9 @@ if !defined(VBOX_ONLY_SDK) && !defined(VBOX_ONLY_EXTPACKS) # Note this goes on f +@@ -1109,7 +1109,9 @@ if !defined(VBOX_ONLY_SDK) && !defined(VBOX_ONLY_EXTPACKS) # Note this goes on f VBoxC_LIBS += \ @@ -1375,11 +1378,39 @@ index 00bab609bf..d98ad1ff8d 100644 VBoxC_LIBS.win += \ $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/psapi.lib \ $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/delayimp.lib +diff --git a/src/VBox/Main/src-server/HostImpl.cpp b/src/VBox/Main/src-server/HostImpl.cpp +index 0440ae7818..98614a295f 100644 +--- a/src/VBox/Main/src-server/HostImpl.cpp ++++ b/src/VBox/Main/src-server/HostImpl.cpp +@@ -82,6 +82,8 @@ + # include + # include + # include ++# include ++# include + #endif /* RT_OS_LINUX */ + + #ifdef RT_OS_SOLARIS +diff --git a/src/VBox/Runtime/Makefile.kmk b/src/VBox/Runtime/Makefile.kmk +index 01237e57bd..e4a7278742 100644 +--- a/src/VBox/Runtime/Makefile.kmk ++++ b/src/VBox/Runtime/Makefile.kmk +@@ -3242,8 +3242,8 @@ if1of ($(KBUILD_TARGET).$(KBUILD_TARGET_ARCH), win.x86 win.amd64 linux.amd64 dar + $(if-expr "$(KBUILD_TARGET_ARCH)" == "amd64",-e "/not-amd64/d",-e "/only-amd64/d") \ + $(if-expr "$(KBUILD_TARGET_ARCH)" == "arm64",-e "/not-arm64/d",-e "/only-arm64/d") \ + $(if-expr "$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)" == "darwin.arm64",, -e "/only-darwin.arm64/d") \ +- $(if-expr "$(substr $(if-expr $(KBUILD_TARGET) != 'win',$(VBOX_GCC_std), $(VBOX_VCC_std)),-2)" >= "17" \ +- ,-e "/before-noexcept/d", -e "/after-noexcept/d") \ ++ $(if-expr "$(VBOX_WITH_KVM)" != "1", $(if-expr "$(substr $(if-expr $(KBUILD_TARGET) != 'win',$(VBOX_GCC_std), $(VBOX_VCC_std)),-2)" >= "17" \ ++ ,-e "/before-noexcept/d", -e "/after-noexcept/d"), -e "/after-noexcept/d") \ + $(if-expr $(intersects $(KBUILD_TARGET), linux) && $(intersects $(KBUILD_TARGET_ARCH), amd64 arm64) \ + ,-e "/int64=llong/d", -e "/int64=long/d") \ + -f "$<" $(filter %.def, $^) diff --git a/src/VBox/Runtime/r3/posix/thread-posix.cpp b/src/VBox/Runtime/r3/posix/thread-posix.cpp -index cd621ce0a1..c89c600e46 100644 +index 0ff4e89cbb..e118e9e2ba 100644 --- a/src/VBox/Runtime/r3/posix/thread-posix.cpp +++ b/src/VBox/Runtime/r3/posix/thread-posix.cpp -@@ -727,6 +727,10 @@ RTDECL(int) RTThreadControlPokeSignal(RTTHREAD hThread, bool fEnable) +@@ -729,6 +729,10 @@ RTDECL(int) RTThreadControlPokeSignal(RTTHREAD hThread, bool fEnable) return rc; } @@ -1391,10 +1422,10 @@ index cd621ce0a1..c89c600e46 100644 #endif diff --git a/src/VBox/Runtime/testcase/Makefile.kmk b/src/VBox/Runtime/testcase/Makefile.kmk -index c3a097b821..4ecc3e535a 100644 +index 66e4101a85..bc71af791c 100644 --- a/src/VBox/Runtime/testcase/Makefile.kmk +++ b/src/VBox/Runtime/testcase/Makefile.kmk -@@ -559,6 +559,7 @@ ifdef VBOX_WITH_TESTCASES # The whole file +@@ -565,6 +565,7 @@ ifdef VBOX_WITH_TESTCASES # The whole file tstLog_CLEAN = $(tstLog_0_OUTDIR)/tstLogGroups.h $$(tstLog_0_OUTDIR)/tstLogGroups.h: $(PATH_ROOT)/include/VBox/log.h $(call MSG_GENERATE,,$@,$<) @@ -1403,7 +1434,7 @@ index c3a097b821..4ecc3e535a 100644 $(QUIET)$(SED) -n -e 's/^ *LOG_GROUP_\([A-Z0-9_]*\),.*$(DOLLAR)/{ LOG_GROUP_\1, "\1" },/p' --output "$@" "$<" endif # !VBOX_ONLY_VALIDATIONKIT diff --git a/src/VBox/VMM/Makefile.kmk b/src/VBox/VMM/Makefile.kmk -index 1d87ac3f2b..43c8b53185 100644 +index 60279b52d3..4f6cfdb33b 100644 --- a/src/VBox/VMM/Makefile.kmk +++ b/src/VBox/VMM/Makefile.kmk @@ -137,7 +137,8 @@ VBoxVMM_SOURCES = \ @@ -1416,7 +1447,7 @@ index 1d87ac3f2b..43c8b53185 100644 VMMR3/GIMKvm.cpp \ VMMR3/GIMMinimal.cpp \ VMMR3/IEMR3.cpp \ -@@ -215,7 +216,8 @@ VBoxVMM_SOURCES = \ +@@ -219,7 +220,8 @@ VBoxVMM_SOURCES = \ VMMAll/EMAll.cpp \ VMMAll/GCMAll.cpp \ VMMAll/GIMAll.cpp \ @@ -1427,7 +1458,7 @@ index 1d87ac3f2b..43c8b53185 100644 VMMAll/TMAll.cpp \ VMMAll/TMAllCpu.cpp \ diff --git a/src/VBox/VMM/VMMAll/APICAll.cpp b/src/VBox/VMM/VMMAll/APICAll.cpp -index 6041a8433a..e2df2ec202 100644 +index 2a36b78359..99ec956da7 100644 --- a/src/VBox/VMM/VMMAll/APICAll.cpp +++ b/src/VBox/VMM/VMMAll/APICAll.cpp @@ -2726,6 +2726,16 @@ VMM_INT_DECL(VBOXSTRICTRC) APICLocalInterrupt(PVMCPUCC pVCpu, uint8_t u8Pin, uin @@ -1449,10 +1480,10 @@ index 6041a8433a..e2df2ec202 100644 if (APICIsEnabled(pVCpu)) diff --git a/src/VBox/VMM/VMMAll/GIMAllHvOnKvm.cpp b/src/VBox/VMM/VMMAll/GIMAllHvOnKvm.cpp new file mode 100644 -index 0000000000..94870277df +index 0000000000..f45a2d7512 --- /dev/null +++ b/src/VBox/VMM/VMMAll/GIMAllHvOnKvm.cpp -@@ -0,0 +1,134 @@ +@@ -0,0 +1,136 @@ +/* + * Copyright (C) Cyberus Technology GmbH. + * @@ -1478,6 +1509,8 @@ index 0000000000..94870277df +#include "GIMInternal.h" +#include + ++#include ++ +#include + +/** @@ -1528,7 +1561,7 @@ index 0000000000..94870277df + return false; +} + -+VMM_INT_DECL(VBOXSTRICTRC) gimHvXcptUD(PVMCPUCC pVCpu, PCPUMCTX pCtx, PDISCPUSTATE pDis, uint8_t *pcbInstr) ++VMM_INT_DECL(VBOXSTRICTRC) gimHvXcptUD(PVMCPUCC pVCpu, PCPUMCTX pCtx, PDISSTATE pDis, uint8_t *pcbInstr) +{ + NOREF(pVCpu); NOREF(pCtx); NOREF(pDis); NOREF(pcbInstr); + AssertLogRelMsgReturn(false, ("%s", __PRETTY_FUNCTION__), VERR_NOT_SUPPORTED); @@ -1588,10 +1621,10 @@ index 0000000000..94870277df + AssertLogRelMsgReturn(false, ("%s", __PRETTY_FUNCTION__), VERR_NOT_SUPPORTED); +} diff --git a/src/VBox/VMM/VMMAll/PGMAllBth.h b/src/VBox/VMM/VMMAll/PGMAllBth.h -index 50b7a30e84..9d4bd8ca62 100644 +index c778bb510c..f95ddcc3d2 100644 --- a/src/VBox/VMM/VMMAll/PGMAllBth.h +++ b/src/VBox/VMM/VMMAll/PGMAllBth.h -@@ -5046,7 +5046,10 @@ PGM_BTH_DECL(int, MapCR3)(PVMCPUCC pVCpu, RTGCPHYS GCPhysCR3) +@@ -5143,7 +5143,10 @@ PGM_BTH_DECL(int, MapCR3)(PVMCPUCC pVCpu, RTGCPHYS GCPhysCR3) || PGM_GST_TYPE == PGM_TYPE_AMD64 LogFlow(("MapCR3: %RGp\n", GCPhysCR3)); @@ -1603,7 +1636,7 @@ index 50b7a30e84..9d4bd8ca62 100644 # if PGM_GST_TYPE == PGM_TYPE_PAE if ( !pVCpu->pgm.s.CTX_SUFF(fPaePdpesAndCr3Mapped) diff --git a/src/VBox/VMM/VMMAll/TMAll.cpp b/src/VBox/VMM/VMMAll/TMAll.cpp -index db39fdaa3d..c0c41c9e64 100644 +index e8a17e3cb9..b57bb4b4eb 100644 --- a/src/VBox/VMM/VMMAll/TMAll.cpp +++ b/src/VBox/VMM/VMMAll/TMAll.cpp @@ -208,7 +208,10 @@ VMMDECL(void) TMNotifyEndOfExecution(PVMCC pVM, PVMCPUCC pVCpu, uint64_t uTsc) @@ -1619,10 +1652,10 @@ index db39fdaa3d..c0c41c9e64 100644 uint64_t cNsExecutingDelta; if (uCpuHz < _4G) diff --git a/src/VBox/VMM/VMMAll/TMAllVirtual.cpp b/src/VBox/VMM/VMMAll/TMAllVirtual.cpp -index 9244bd850b..2e34aeaf6d 100644 +index 15c1b70c6c..ae9384578e 100644 --- a/src/VBox/VMM/VMMAll/TMAllVirtual.cpp +++ b/src/VBox/VMM/VMMAll/TMAllVirtual.cpp -@@ -952,7 +952,11 @@ VMM_INT_DECL(uint64_t) TMVirtualSyncGetWithDeadlineNoCheck(PVMCC pVM, uint64_t * +@@ -955,7 +955,11 @@ VMM_INT_DECL(uint64_t) TMVirtualSyncGetWithDeadlineNoCheck(PVMCC pVM, uint64_t * VMMDECL(uint64_t) TMVirtualSyncGetNsToDeadline(PVMCC pVM, uint64_t *puDeadlineVersion, uint64_t *puTscNow) { uint64_t cNsToDeadline; @@ -1635,7 +1668,7 @@ index 9244bd850b..2e34aeaf6d 100644 } diff --git a/src/VBox/VMM/VMMR3/APIC.cpp b/src/VBox/VMM/VMMR3/APIC.cpp -index 6753ac6080..b5ff86cc7f 100644 +index 14964921f2..bcdbe9d83b 100644 --- a/src/VBox/VMM/VMMR3/APIC.cpp +++ b/src/VBox/VMM/VMMR3/APIC.cpp @@ -36,6 +36,7 @@ @@ -1702,10 +1735,10 @@ index 6753ac6080..b5ff86cc7f 100644 APIC_REG_COUNTER(&pApicCpu->StatPostIntrCnt, "%u", "APIC/VCPU stats / number of apicPostInterrupt calls."); for (size_t i = 0; i < RT_ELEMENTS(pApicCpu->aStatVectors); i++) diff --git a/src/VBox/VMM/VMMR3/CPUM.cpp b/src/VBox/VMM/VMMR3/CPUM.cpp -index 7e0fbd32fb..f906332e72 100644 +index a3aecdefd7..12c1c3b792 100644 --- a/src/VBox/VMM/VMMR3/CPUM.cpp +++ b/src/VBox/VMM/VMMR3/CPUM.cpp -@@ -1790,9 +1790,13 @@ void cpumR3InitVmxGuestFeaturesAndMsrs(PVM pVM, PCFGMNODE pCpumCfg, PCVMXMSRS pH +@@ -1842,9 +1842,13 @@ void cpumR3InitVmxGuestFeaturesAndMsrs(PVM pVM, PCFGMNODE pCpumCfg, PCVMXMSRS pH if (fVmxEpt) { const char *pszWhy = NULL; @@ -1719,7 +1752,7 @@ index 7e0fbd32fb..f906332e72 100644 pszWhy = "nested paging is not enabled for the VM or it is not supported by the host"; else if (VM_IS_HM_ENABLED(pVM) && !pVM->cpum.s.HostFeatures.fNoExecute) pszWhy = "NX is not available on the host"; -@@ -2845,10 +2849,21 @@ static DECLCALLBACK(int) cpumR3LoadExec(PVM pVM, PSSMHANDLE pSSM, uint32_t uVers +@@ -2996,10 +3000,21 @@ static DECLCALLBACK(int) cpumR3LoadExec(PVM pVM, PSSMHANDLE pSSM, uint32_t uVers rc = SSMR3GetStructEx(pSSM, &pGstCtx->XState.Hdr, sizeof(pGstCtx->XState.Hdr), 0, g_aCpumXSaveHdrFields, NULL); AssertRCReturn(rc, rc); @@ -1742,10 +1775,10 @@ index 7e0fbd32fb..f906332e72 100644 if (pGstCtx->fXStateMask & XSAVE_C_YMM) { diff --git a/src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp b/src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp -index 04d8ac3cae..5263cb1014 100644 +index 455f55c70c..884247ee96 100644 --- a/src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp +++ b/src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp -@@ -1331,6 +1331,13 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) +@@ -1341,6 +1341,13 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) ((enmConfig) && ((enmConfig) == CPUMISAEXTCFG_ENABLED_ALWAYS || (fHostFeature)) && (fAndExpr) ? (fConst) : 0) #define PASSTHRU_FEATURE_TODO(enmConfig, fConst) ((enmConfig) ? (fConst) : 0) @@ -1759,25 +1792,7 @@ index 04d8ac3cae..5263cb1014 100644 /* Cpuid 1: * EAX: CPU model, family and stepping. * -@@ -1390,7 +1397,7 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) - //| X86_CPUID_FEATURE_ECX_TM2 - no thermal monitor 2. - | X86_CPUID_FEATURE_ECX_SSSE3 - //| X86_CPUID_FEATURE_ECX_CNTXID - no L1 context id (MSR++). -- //| X86_CPUID_FEATURE_ECX_FMA - not implemented yet. -+ | PASSTHRU_FEATURE_KVM_ONLY(X86_CPUID_FEATURE_ECX_FMA) - | PASSTHRU_FEATURE(pConfig->enmCmpXchg16b, pHstFeat->fMovCmpXchg16b, X86_CPUID_FEATURE_ECX_CX16) - /* ECX Bit 14 - xTPR Update Control. Processor supports changing IA32_MISC_ENABLES[bit 23]. */ - //| X86_CPUID_FEATURE_ECX_TPRUPDATE -@@ -1407,7 +1414,7 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) - | PASSTHRU_FEATURE(pConfig->enmXSave, pHstFeat->fXSaveRstor, X86_CPUID_FEATURE_ECX_XSAVE) - //| X86_CPUID_FEATURE_ECX_OSXSAVE - mirrors CR4.OSXSAVE state, set dynamically. - | PASSTHRU_FEATURE(pConfig->enmAvx, pHstFeat->fAvx, X86_CPUID_FEATURE_ECX_AVX) -- //| X86_CPUID_FEATURE_ECX_F16C - not implemented yet. -+ | PASSTHRU_FEATURE_KVM_ONLY(X86_CPUID_FEATURE_ECX_F16C) - | PASSTHRU_FEATURE_TODO(pConfig->enmRdRand, X86_CPUID_FEATURE_ECX_RDRAND) - //| X86_CPUID_FEATURE_ECX_HVP - Set explicitly later. - ; -@@ -1590,7 +1597,7 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) +@@ -1600,7 +1607,7 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) | X86_CPUID_AMD_FEATURE_EDX_MMX | X86_CPUID_AMD_FEATURE_EDX_FXSR | X86_CPUID_AMD_FEATURE_EDX_FFXSR @@ -1786,7 +1801,7 @@ index 04d8ac3cae..5263cb1014 100644 | X86_CPUID_EXT_FEATURE_EDX_RDTSCP //| RT_BIT_32(28) - reserved //| X86_CPUID_EXT_FEATURE_EDX_LONG_MODE - turned on when necessary -@@ -1852,9 +1859,9 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) +@@ -1862,9 +1869,9 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) //| X86_CPUID_STEXT_FEATURE_EBX_HLE RT_BIT(4) | PASSTHRU_FEATURE(pConfig->enmAvx2, pHstFeat->fAvx2, X86_CPUID_STEXT_FEATURE_EBX_AVX2) | X86_CPUID_STEXT_FEATURE_EBX_FDP_EXCPTN_ONLY @@ -1798,13 +1813,10 @@ index 04d8ac3cae..5263cb1014 100644 | PASSTHRU_FEATURE(pConfig->enmInvpcid, pHstFeat->fInvpcid, X86_CPUID_STEXT_FEATURE_EBX_INVPCID) //| X86_CPUID_STEXT_FEATURE_EBX_RTM RT_BIT(11) //| X86_CPUID_STEXT_FEATURE_EBX_PQM RT_BIT(12) -@@ -1864,29 +1871,33 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) - //| X86_CPUID_STEXT_FEATURE_EBX_AVX512F RT_BIT(16) - //| RT_BIT(17) - reserved +@@ -1876,10 +1883,11 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) | PASSTHRU_FEATURE_TODO(pConfig->enmRdSeed, X86_CPUID_STEXT_FEATURE_EBX_RDSEED) -- //| X86_CPUID_STEXT_FEATURE_EBX_ADX RT_BIT(19) -- //| X86_CPUID_STEXT_FEATURE_EBX_SMAP RT_BIT(20) -+ | PASSTHRU_FEATURE_KVM_ONLY(X86_CPUID_STEXT_FEATURE_EBX_ADX) + | PASSTHRU_FEATURE(pConfig->enmAdx, pHstFeat->fAdx, X86_CPUID_STEXT_FEATURE_EBX_ADX) + //| X86_CPUID_STEXT_FEATURE_EBX_SMAP RT_BIT(20) + | PASSTHRU_FEATURE_KVM_ONLY(X86_CPUID_STEXT_FEATURE_EBX_SMAP) //| RT_BIT(21) - reserved //| RT_BIT(22) - reserved @@ -1814,11 +1826,7 @@ index 04d8ac3cae..5263cb1014 100644 //| X86_CPUID_STEXT_FEATURE_EBX_INTEL_PT RT_BIT(25) //| X86_CPUID_STEXT_FEATURE_EBX_AVX512PF RT_BIT(26) //| X86_CPUID_STEXT_FEATURE_EBX_AVX512ER RT_BIT(27) - //| X86_CPUID_STEXT_FEATURE_EBX_AVX512CD RT_BIT(28) -- //| X86_CPUID_STEXT_FEATURE_EBX_SHA RT_BIT(29) -+ | PASSTHRU_FEATURE_KVM_ONLY(X86_CPUID_STEXT_FEATURE_EBX_SHA) - //| RT_BIT(30) - reserved - //| RT_BIT(31) - reserved +@@ -1890,13 +1898,17 @@ static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig) ; pCurLeaf->uEcx &= 0 //| X86_CPUID_STEXT_FEATURE_ECX_PREFETCHWT1 - we do not do vector functions yet. @@ -1838,7 +1846,7 @@ index 04d8ac3cae..5263cb1014 100644 ; /* Mask out INVPCID unless FSGSBASE is exposed due to a bug in Windows 10 SMP guests, see @bugref{9089#c15}. */ -@@ -2787,6 +2798,7 @@ static int cpumR3CpuIdReadConfig(PVM pVM, PCPUMCPUIDCONFIG pConfig, PCFGMNODE pC +@@ -2811,6 +2823,7 @@ static int cpumR3CpuIdReadConfig(PVM pVM, PCPUMCPUIDCONFIG pConfig, PCFGMNODE pC AssertLogRelRCReturn(rc, rc); if (pConfig->fNestedHWVirt) { @@ -1846,7 +1854,7 @@ index 04d8ac3cae..5263cb1014 100644 /** @todo Think about enabling this later with NEM/KVM. */ if (VM_IS_NEM_ENABLED(pVM)) { -@@ -2796,6 +2808,7 @@ static int cpumR3CpuIdReadConfig(PVM pVM, PCPUMCPUIDCONFIG pConfig, PCFGMNODE pC +@@ -2820,6 +2833,7 @@ static int cpumR3CpuIdReadConfig(PVM pVM, PCPUMCPUIDCONFIG pConfig, PCFGMNODE pC else if (!fNestedPagingAndFullGuestExec) return VMSetError(pVM, VERR_CPUM_INVALID_HWVIRT_CONFIG, RT_SRC_POS, "Cannot enable nested VT-x/AMD-V without nested-paging and unrestricted guest execution!\n"); @@ -1854,7 +1862,7 @@ index 04d8ac3cae..5263cb1014 100644 } } -@@ -3384,6 +3397,7 @@ VMMR3_INT_DECL(void) CPUMR3SetGuestCpuIdFeature(PVM pVM, CPUMCPUIDFEATURE enmFea +@@ -3736,6 +3750,7 @@ VMMR3_INT_DECL(void) CPUMR3SetGuestCpuIdFeature(PVM pVM, CPUMCPUIDFEATURE enmFea * Note! ASSUMES CPUMCPUIDFEATURE_APIC is called first. */ case CPUMCPUIDFEATURE_X2APIC: @@ -1862,7 +1870,7 @@ index 04d8ac3cae..5263cb1014 100644 pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x00000001)); if (pLeaf) pVM->cpum.s.aGuestCpuIdPatmStd[1].uEcx = pLeaf->uEcx |= X86_CPUID_FEATURE_ECX_X2APIC; -@@ -3398,6 +3412,7 @@ VMMR3_INT_DECL(void) CPUMR3SetGuestCpuIdFeature(PVM pVM, CPUMCPUIDFEATURE enmFea +@@ -3750,6 +3765,7 @@ VMMR3_INT_DECL(void) CPUMR3SetGuestCpuIdFeature(PVM pVM, CPUMCPUIDFEATURE enmFea } LogRel(("CPUM: SetGuestCpuIdFeature: Enabled x2APIC\n")); @@ -1871,10 +1879,10 @@ index 04d8ac3cae..5263cb1014 100644 /* diff --git a/src/VBox/VMM/VMMR3/EM.cpp b/src/VBox/VMM/VMMR3/EM.cpp -index bd15b8872c..5e52065aab 100644 +index b85b21f730..79ae5a4015 100644 --- a/src/VBox/VMM/VMMR3/EM.cpp +++ b/src/VBox/VMM/VMMR3/EM.cpp -@@ -219,7 +219,11 @@ VMMR3_INT_DECL(int) EMR3Init(PVM pVM) +@@ -223,7 +223,11 @@ VMMR3_INT_DECL(int) EMR3Init(PVM pVM) { PVMCPU pVCpu = pVM->apCpusR3[idCpu]; @@ -1884,9 +1892,9 @@ index bd15b8872c..5e52065aab 100644 pVCpu->em.s.enmState = idCpu == 0 ? EMSTATE_NONE : EMSTATE_WAIT_SIPI; +#endif pVCpu->em.s.enmPrevState = EMSTATE_NONE; - pVCpu->em.s.u64TimeSliceStart = 0; /* paranoia */ + pVCpu->em.s.msTimeSliceStart = 0; /* paranoia */ pVCpu->em.s.idxContinueExitRec = UINT16_MAX; -@@ -2353,7 +2357,14 @@ VMMR3_INT_DECL(int) EMR3ExecuteVM(PVM pVM, PVMCPU pVCpu) +@@ -2322,7 +2326,14 @@ VMMR3_INT_DECL(int) EMR3ExecuteVM(PVM pVM, PVMCPU pVCpu) else { /* All other VCPUs go into the wait for SIPI state. */ @@ -1902,7 +1910,7 @@ index bd15b8872c..5e52065aab 100644 break; } diff --git a/src/VBox/VMM/VMMR3/GIMHv.cpp b/src/VBox/VMM/VMMR3/GIMHv.cpp -index 0452facbe3..1da9065120 100644 +index 3ace6c2b48..6efc37b1eb 100644 --- a/src/VBox/VMM/VMMR3/GIMHv.cpp +++ b/src/VBox/VMM/VMMR3/GIMHv.cpp @@ -34,6 +34,9 @@ @@ -2673,10 +2681,10 @@ index 0000000000..362cc690f1 + return VERR_GIM_NO_DEBUG_CONNECTION; +} diff --git a/src/VBox/VMM/VMMR3/NEMR3Native-linux.cpp b/src/VBox/VMM/VMMR3/NEMR3Native-linux.cpp -index fa73141590..42c44765e0 100644 +index 4146266129..5fd74f256d 100644 --- a/src/VBox/VMM/VMMR3/NEMR3Native-linux.cpp +++ b/src/VBox/VMM/VMMR3/NEMR3Native-linux.cpp -@@ -37,21 +37,32 @@ +@@ -37,30 +37,124 @@ #include #include #include @@ -2702,22 +2710,21 @@ index fa73141590..42c44765e0 100644 +#include #include +- + /* Forward declarations of things called by the template. */ + static int nemR3LnxInitSetupVm(PVM pVM, PRTERRINFO pErrInfo); +#include +#include +#include -+ - /* - * Supply stuff missing from the kvm.h on the build box. - */ -@@ -59,7 +70,19 @@ - # define KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON 4 - #endif +/** + * The MMIO address of the TPR register of the LAPIC. + */ +static constexpr uint64_t XAPIC_TPR_ADDR {0xfee00080}; + /* Instantiate the common bits we share with the ARMv8 KVM backend. */ + #include "NEMR3NativeTemplate-linux.cpp.h" + +/** + * The class priority shift for the TPR register. + */ @@ -2726,13 +2733,9 @@ index fa73141590..42c44765e0 100644 +#ifdef VBOX_WITH_KVM_IRQCHIP_FULL +static int kvmSetGsiRoutingFullIrqChip(PVM pVM); +#endif - - /** - * Worker for nemR3NativeInit that gets the hypervisor capabilities. -@@ -415,6 +438,78 @@ static int nemR3LnxInitCheckCapabilities(PVM pVM, PRTERRINFO pErrInfo) - } - - ++ ++ ++ +#ifdef VBOX_WITH_KVM_NESTING +static int KvmGetGuestModeOffsetFromStatsFd(PVMCPU pVCpu, size_t *offset) +{ @@ -2792,7 +2795,7 @@ index fa73141590..42c44765e0 100644 + } + + uint64_t value {0}; -+ + + AssertReleaseMsg(pVCpu->nem.s.guestModeStatOffset != 0, ("Invalid guest_mode offset")); + + int rcRead = pread(pVCpu->nem.s.statsFd, &value, 8, pVCpu->nem.s.guestModeStatOffset); @@ -2804,11 +2807,10 @@ index fa73141590..42c44765e0 100644 + return false; +#endif +} -+ + /** * Does the early setup of a KVM VM. - * -@@ -439,6 +534,23 @@ static int nemR3LnxInitSetupVm(PVM pVM, PRTERRINFO pErrInfo) +@@ -86,6 +180,23 @@ static int nemR3LnxInitSetupVm(PVM pVM, PRTERRINFO pErrInfo) if (rcLnx == -1) return RTErrInfoSetF(pErrInfo, VERR_NEM_VM_CREATE_FAILED, "Failed to enable KVM_CAP_X86_USER_SPACE_MSR failed: %u", errno); @@ -2832,7 +2834,7 @@ index fa73141590..42c44765e0 100644 /* * Create the VCpus. */ -@@ -459,20 +571,138 @@ static int nemR3LnxInitSetupVm(PVM pVM, PRTERRINFO pErrInfo) +@@ -106,10 +217,128 @@ static int nemR3LnxInitSetupVm(PVM pVM, PRTERRINFO pErrInfo) /* We want all x86 registers and events on each exit. */ pVCpu->nem.s.pRun->kvm_valid_regs = KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS | KVM_SYNC_X86_EVENTS; @@ -2862,28 +2864,6 @@ index fa73141590..42c44765e0 100644 return VINF_SUCCESS; } -+static VBOXSTRICTRC nemR3LnxSetVCpuSignalMask(PVMCPU pVCpu, sigset_t *pSigset) -+{ -+ /* -+ * glibc and Linux/KVM do not agree on the size of sigset_t. -+ */ -+ constexpr size_t kernel_sigset_size = 8; -+ -+ alignas(kvm_signal_mask) char backing[sizeof(kvm_signal_mask) + kernel_sigset_size]; -+ kvm_signal_mask *pKvmSignalMask = reinterpret_cast(backing); -+ -+ static_assert(sizeof(sigset_t) >= kernel_sigset_size); -+ -+ pKvmSignalMask->len = kernel_sigset_size; -+ memcpy(pKvmSignalMask->sigset, pSigset, kernel_sigset_size); -+ -+ int rc = ioctl(pVCpu->nem.s.fdVCpu, KVM_SET_SIGNAL_MASK, pKvmSignalMask); -+ AssertLogRelMsgReturn(rc == 0, ("Failed to set vCPU signal mask: %d", errno), -+ VERR_NEM_INIT_FAILED); -+ -+ return VINF_SUCCESS; -+} -+ +static void nemR3LnxConsumePokeSignal() +{ + int iPokeSignal = RTThreadPokeSignal(); @@ -2902,100 +2882,7 @@ index fa73141590..42c44765e0 100644 + int rc = sigtimedwait(&sigset, nullptr, &timeout); + AssertLogRelMsg(rc >= 0 || errno == EAGAIN || errno == EINTR, ("Failed to consume signal: %d", errno)); +} - - /** @callback_method_impl{FNVMMEMTRENDEZVOUS} */ - static DECLCALLBACK(VBOXSTRICTRC) nemR3LnxFixThreadPoke(PVM pVM, PVMCPU pVCpu, void *pvUser) - { - RT_NOREF(pVM, pvUser); -- int rc = RTThreadControlPokeSignal(pVCpu->hThread, true /*fEnable*/); -+ -+ int iPokeSignal = RTThreadPokeSignal(); -+ AssertReturn(iPokeSignal >= 0, VERR_NEM_INIT_FAILED); -+ -+ /* We disable the poke signal for the host. We never want that signal to be delivered. */ -+ int rc = RTThreadControlPokeSignal(pVCpu->hThread, false /*fEnable*/); - AssertLogRelRC(rc); -+ -+ sigset_t sigset; -+ -+ /* Fetch the current signal mask. */ -+ int rcProcMask = pthread_sigmask(SIG_BLOCK /* ignored */, nullptr, &sigset); -+ AssertLogRelMsgReturn(rcProcMask == 0, ("Failed to retrieve thread signal mask"), VERR_NEM_INIT_FAILED); -+ -+ sigdelset(&sigset, iPokeSignal); -+ -+ /* We enable the poke signal for the vCPU. Any poke will kick the vCPU out of guest execution. */ -+ VBOXSTRICTRC rcVcpuMask = nemR3LnxSetVCpuSignalMask(pVCpu, &sigset); -+ AssertRCSuccessReturn(rcVcpuMask, rcVcpuMask); -+ -+ /* Create a timer that delivers the poke signal. */ -+ struct sigevent sev {}; -+ -+ sev.sigev_notify = SIGEV_THREAD_ID; -+ sev.sigev_signo = iPokeSignal; -+ sev._sigev_un._tid = gettid(); -+ -+ int rcTimer = timer_create(CLOCK_MONOTONIC, &sev, &pVCpu->nem.s.pTimer); -+ AssertLogRelMsgReturn(rcTimer == 0, ("Failed to create timer: %d", errno), VERR_NEM_INIT_FAILED); -+ - return VINF_SUCCESS; - } - -+/** -+ * Check common environment problems and inform the user about misconfigurations. -+ */ -+int nemR3CheckEnvironment(void) -+{ -+ static const char szSplitLockMitigationFile[] = "/proc/sys/kernel/split_lock_mitigate"; -+ -+ char buf[64] {}; -+ int fd = open(szSplitLockMitigationFile, O_RDONLY | O_CLOEXEC); -+ -+ // Older kernels might not have this. A hard error feels unjustified here. -+ AssertLogRelMsgReturn(fd >= 0, ("Failed to check %s (%d). Assuming there is no problem.\n", szSplitLockMitigationFile, fd), -+ VINF_SUCCESS); -+ -+ /* Leave one character to ensure that the string is zero-terminated. */ -+ ssize_t bytes = read(fd, buf, sizeof(buf) - 1); -+ AssertLogRelMsgReturn(bytes >= 0, ("Failed to read %s (%zd)\n", szSplitLockMitigationFile, bytes), -+ VERR_NEM_INIT_FAILED); -+ -+ int mitigationStatus = atoi(buf); -+ -+ if (mitigationStatus != 0) { -+ LogRel(("NEM: WARNING: %s is %d. This can cause VM hangs, unless you set split_lock_detect=off on the host kernel command line! Please set it to 0.\n", -+ szSplitLockMitigationFile, mitigationStatus)); -+ } -+ -+ return VINF_SUCCESS; -+} - - /** - * Try initialize the native API. -@@ -490,6 +720,10 @@ static DECLCALLBACK(VBOXSTRICTRC) nemR3LnxFixThreadPoke(PVM pVM, PVMCPU pVCpu, v - int nemR3NativeInit(PVM pVM, bool fFallback, bool fForced) - { - RT_NOREF(pVM, fFallback, fForced); + -+ int rcCheck = nemR3CheckEnvironment(); -+ AssertLogRelMsgReturn(RT_SUCCESS(rcCheck), ("Failed to check environment\n"), VERR_NEM_INIT_FAILED); -+ - /* - * Some state init. - */ -@@ -600,7 +834,7 @@ int nemR3NativeInit(PVM pVM, bool fFallback, bool fForced) - else if (errno == EACCES) - rc = RTErrInfoSet(pErrInfo, VERR_ACCESS_DENIED, "Do not have access to open /dev/kvm for reading & writing."); - else if (errno == ENOENT) -- rc = RTErrInfoSet(pErrInfo, VERR_NOT_SUPPORTED, "KVM is not availble (/dev/kvm does not exist)"); -+ rc = RTErrInfoSet(pErrInfo, VERR_NOT_SUPPORTED, "KVM is not available (/dev/kvm does not exist)"); - else - rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromErrno(errno), "Failed to open '/dev/kvm': %u", errno); - -@@ -636,6 +870,83 @@ int nemR3NativeInitAfterCPUM(PVM pVM) - return VINF_SUCCESS; - } - +static PCPUMCPUIDLEAF findKvmLeaf(PCPUMCPUIDLEAF paKvmSupportedLeaves, + uint32_t cKvmSupportedLeaves, + uint32_t leaf, @@ -3076,7 +2963,7 @@ index fa73141590..42c44765e0 100644 /** * Update the CPUID leaves for a VCPU. -@@ -654,6 +965,12 @@ static int nemR3LnxUpdateCpuIdsLeaves(PVM pVM, PVMCPU pVCpu) +@@ -128,6 +357,12 @@ static int nemR3LnxUpdateCpuIdsLeaves(PVM pVM, PVMCPU pVCpu) pReq->nent = cLeaves; pReq->padding = 0; @@ -3089,7 +2976,7 @@ index fa73141590..42c44765e0 100644 for (uint32_t i = 0; i < cLeaves; i++) { CPUMGetGuestCpuId(pVCpu, paLeaves[i].uLeaf, paLeaves[i].uSubLeaf, -1 /*f64BitMode*/, -@@ -661,6 +978,16 @@ static int nemR3LnxUpdateCpuIdsLeaves(PVM pVM, PVMCPU pVCpu) +@@ -135,6 +370,16 @@ static int nemR3LnxUpdateCpuIdsLeaves(PVM pVM, PVMCPU pVCpu) &pReq->entries[i].ebx, &pReq->entries[i].ecx, &pReq->entries[i].edx); @@ -3106,7 +2993,7 @@ index fa73141590..42c44765e0 100644 pReq->entries[i].function = paLeaves[i].uLeaf; pReq->entries[i].index = paLeaves[i].uSubLeaf; pReq->entries[i].flags = !paLeaves[i].fSubLeafMask ? 0 : KVM_CPUID_FLAG_SIGNIFCANT_INDEX; -@@ -675,6 +1002,111 @@ static int nemR3LnxUpdateCpuIdsLeaves(PVM pVM, PVMCPU pVCpu) +@@ -149,6 +394,111 @@ static int nemR3LnxUpdateCpuIdsLeaves(PVM pVM, PVMCPU pVCpu) return VINF_SUCCESS; } @@ -3218,7 +3105,7 @@ index fa73141590..42c44765e0 100644 int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) { -@@ -692,11 +1124,32 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) +@@ -166,11 +516,32 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) { for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) { @@ -3251,7 +3138,7 @@ index fa73141590..42c44765e0 100644 /* * Configure MSRs after ring-3 init is done. * -@@ -725,6 +1178,8 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) +@@ -199,6 +570,8 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) MsrFilters.ranges[iRange].bitmap = (uint8_t *)&RT_CONCAT(bm, a_uBase)[0] #define MSR_RANGE_ADD(a_Msr) \ do { Assert((uint32_t)(a_Msr) - uBase < cMsrs); ASMBitSet(pbm, (uint32_t)(a_Msr) - uBase); } while (0) @@ -3260,7 +3147,7 @@ index fa73141590..42c44765e0 100644 #define MSR_RANGE_END(a_cMinMsrs) \ /* optimize the range size before closing: */ \ uint32_t cBitmap = cMsrs / 64; \ -@@ -736,11 +1191,45 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) +@@ -210,11 +583,45 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) /* 1st Intel range: 0000_0000 to 0000_3000. */ MSR_RANGE_BEGIN(0x00000000, 0x00003000, KVM_MSR_FILTER_READ | KVM_MSR_FILTER_WRITE); @@ -3306,7 +3193,7 @@ index fa73141590..42c44765e0 100644 /** @todo more? */ MSR_RANGE_END(64); -@@ -748,6 +1237,13 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) +@@ -222,6 +629,13 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) MSR_RANGE_BEGIN(0xc0000000, 0xc0003000, KVM_MSR_FILTER_READ | KVM_MSR_FILTER_WRITE); MSR_RANGE_ADD(MSR_K6_EFER); MSR_RANGE_ADD(MSR_K6_STAR); @@ -3320,7 +3207,7 @@ index fa73141590..42c44765e0 100644 MSR_RANGE_ADD(MSR_K8_GS_BASE); MSR_RANGE_ADD(MSR_K8_KERNEL_GS_BASE); MSR_RANGE_ADD(MSR_K8_LSTAR); -@@ -757,6 +1253,49 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) +@@ -231,6 +645,49 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) /** @todo add more? */ MSR_RANGE_END(64); @@ -3370,133 +3257,14 @@ index fa73141590..42c44765e0 100644 /** @todo Specify other ranges too? Like hyper-V and KVM to make sure we get * the MSR requests instead of KVM. */ -@@ -805,6 +1344,9 @@ int nemR3NativeTerm(PVM pVM) - close(pVM->nem.s.fdKvm); - pVM->nem.s.fdKvm = -1; - } -+ -+ pVM->nem.s.pARedirectionTable.reset(); -+ - return VINF_SUCCESS; +@@ -244,6 +701,330 @@ int nemR3NativeInitCompleted(PVM pVM, VMINITCOMPLETED enmWhat) } -@@ -816,7 +1358,18 @@ int nemR3NativeTerm(PVM pVM) - */ - void nemR3NativeReset(PVM pVM) - { -- RT_NOREF(pVM); -+ pVM->nem.s.pARedirectionTable->fill(std::nullopt); -+ -+ for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) -+ { -+ PVMCPU pVCpu = pVM->apCpusR3[idCpu]; -+ -+ struct kvm_mp_state mp; -+ mp.mp_state = pVCpu->idCpu == 0 ? KVM_MP_STATE_RUNNABLE : KVM_MP_STATE_UNINITIALIZED; -+ -+ int rcLnx = ioctl(pVCpu->nem.s.fdVCpu, KVM_SET_MP_STATE, &mp); -+ AssertLogRelMsg(rcLnx == 0, ("nemR3NativeReset: Failed to set MP state. Error: %d, errno %d\n", rcLnx, errno)); -+ } - } - - -@@ -1030,94 +1583,413 @@ VMMR3_INT_DECL(int) NEMR3NotifyPhysMmioExUnmap(PVM pVM, RTGCPHYS GCPhys, RTGCPHY - return VINF_SUCCESS; - } -- --VMMR3_INT_DECL(int) NEMR3PhysMmio2QueryAndResetDirtyBitmap(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, uint32_t uNemRange, -- void *pvBitmap, size_t cbBitmap) -+ -+VMMR3_INT_DECL(int) NEMR3PhysMmio2QueryAndResetDirtyBitmap(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, uint32_t uNemRange, -+ void *pvBitmap, size_t cbBitmap) -+{ -+ AssertReturn(uNemRange > 0 && uNemRange < _32K, VERR_NEM_IPE_4); -+ AssertReturn(ASMBitTest(pVM->nem.s.bmSlotIds, uNemRange), VERR_NEM_IPE_4); -+ -+ RT_NOREF(GCPhys, cbBitmap); -+ -+ struct kvm_dirty_log DirtyLog; -+ DirtyLog.slot = uNemRange; -+ DirtyLog.padding1 = 0; -+ DirtyLog.dirty_bitmap = pvBitmap; -+ -+ int rc = ioctl(pVM->nem.s.fdVm, KVM_GET_DIRTY_LOG, &DirtyLog); -+ AssertLogRelMsgReturn(rc == 0, ("%RGp LB %RGp idSlot=%#x failed: %u/%u\n", GCPhys, cb, uNemRange, errno, rc), -+ VERR_NEM_QUERY_DIRTY_BITMAP_FAILED); -+ -+ return VINF_SUCCESS; -+} -+ -+ -+VMMR3_INT_DECL(int) NEMR3NotifyPhysRomRegisterEarly(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, void *pvPages, uint32_t fFlags, -+ uint8_t *pu2State, uint32_t *puNemRange) -+{ -+ Log5(("NEMR3NotifyPhysRomRegisterEarly: %RGp LB %RGp pvPages=%p fFlags=%#x\n", GCPhys, cb, pvPages, fFlags)); -+ *pu2State = UINT8_MAX; -+ -+ /* Don't support puttint ROM where there is already RAM. For -+ now just shuffle the registrations till it works... */ -+ AssertLogRelMsgReturn(!(fFlags & NEM_NOTIFY_PHYS_ROM_F_REPLACE), ("%RGp LB %RGp fFlags=%#x\n", GCPhys, cb, fFlags), -+ VERR_NEM_MAP_PAGES_FAILED); -+ -+ /** @todo figure out how to do shadow ROMs. */ -+ -+ /* -+ * We only allocate a slot number here in case we need to use it to -+ * fend of physical handler fun. -+ */ -+ uint16_t idSlot = nemR3LnxMemSlotIdAlloc(pVM); -+ AssertLogRelReturn(idSlot < _32K, VERR_NEM_MAP_PAGES_FAILED); -+ -+ *pu2State = 0; -+ *puNemRange = idSlot; -+ Log5(("NEMR3NotifyPhysRomRegisterEarly: %RGp LB %RGp fFlags=%#x pvPages=%p - idSlot=%#x\n", -+ GCPhys, cb, fFlags, pvPages, idSlot)); -+ RT_NOREF(GCPhys, cb, fFlags, pvPages); -+ return VINF_SUCCESS; -+} -+ -+ -+VMMR3_INT_DECL(int) NEMR3NotifyPhysRomRegisterLate(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, void *pvPages, -+ uint32_t fFlags, uint8_t *pu2State, uint32_t *puNemRange) -+{ -+ Log5(("NEMR3NotifyPhysRomRegisterLate: %RGp LB %RGp pvPages=%p fFlags=%#x pu2State=%p (%d) puNemRange=%p (%#x)\n", -+ GCPhys, cb, pvPages, fFlags, pu2State, *pu2State, puNemRange, *puNemRange)); -+ -+ AssertPtrReturn(pvPages, VERR_NEM_IPE_5); -+ -+ uint32_t const idSlot = *puNemRange; -+ AssertReturn(idSlot > 0 && idSlot < _32K, VERR_NEM_IPE_4); -+ AssertReturn(ASMBitTest(pVM->nem.s.bmSlotIds, idSlot), VERR_NEM_IPE_4); -+ -+ *pu2State = UINT8_MAX; -+ -+ /* -+ * Do the actual setting of the user pages here now that we've -+ * got a valid pvPages (typically isn't available during the early -+ * notification, unless we're replacing RAM). -+ */ -+ struct kvm_userspace_memory_region Region; -+ Region.slot = idSlot; -+ Region.flags = 0; -+ Region.guest_phys_addr = GCPhys; -+ Region.memory_size = cb; -+ Region.userspace_addr = (uintptr_t)pvPages; -+ -+ int rc = ioctl(pVM->nem.s.fdVm, KVM_SET_USER_MEMORY_REGION, &Region); -+ if (rc == 0) -+ { -+ *pu2State = 0; -+ Log5(("NEMR3NotifyPhysRomRegisterEarly: %RGp LB %RGp fFlags=%#x pvPages=%p - idSlot=%#x\n", -+ GCPhys, cb, fFlags, pvPages, idSlot)); -+ return VINF_SUCCESS; -+ } -+ AssertLogRelMsgFailedReturn(("%RGp LB %RGp fFlags=%#x, pvPages=%p, idSlot=%#x failed: %u/%u\n", -+ GCPhys, cb, fFlags, pvPages, idSlot, errno, rc), -+ VERR_NEM_MAP_PAGES_FAILED); -+} + ++/********************************************************************************************************************************* ++* Memory management * ++*********************************************************************************************************************************/ + +VMMR3_INT_DECL(int) NEMR3LoadExec(PVM pVM) +{ @@ -3739,29 +3507,19 @@ index fa73141590..42c44765e0 100644 +} + +VMMR3_INT_DECL(int) NEMR3KvmSetIoApicState(PVM pVM, KVMIOAPICSTATE* state) - { -- AssertReturn(uNemRange > 0 && uNemRange < _32K, VERR_NEM_IPE_4); -- AssertReturn(ASMBitTest(pVM->nem.s.bmSlotIds, uNemRange), VERR_NEM_IPE_4); ++{ + struct kvm_irqchip irqchip_state; + irqchip_state.chip_id = KVM_IRQCHIP_IOAPIC; - -- RT_NOREF(GCPhys, cbBitmap); ++ + if (state == nullptr) { + return VERR_INVALID_POINTER; + } - -- struct kvm_dirty_log DirtyLog; -- DirtyLog.slot = uNemRange; -- DirtyLog.padding1 = 0; -- DirtyLog.dirty_bitmap = pvBitmap; ++ + irqchip_state.chip.ioapic.base_address = state->base_address; + irqchip_state.chip.ioapic.ioregsel = state->ioregsel; + irqchip_state.chip.ioapic.id = state->id; + irqchip_state.chip.ioapic.irr = state->irr; - -- int rc = ioctl(pVM->nem.s.fdVm, KVM_GET_DIRTY_LOG, &DirtyLog); -- AssertLogRelMsgReturn(rc == 0, ("%RGp LB %RGp idSlot=%#x failed: %u/%u\n", GCPhys, cb, uNemRange, errno, rc), -- VERR_NEM_QUERY_DIRTY_BITMAP_FAILED); ++ + for (unsigned i = 0; i < KVM_IRQCHIP_NUM_IOAPIC_INTR_PINS; ++i) { + irqchip_state.chip.ioapic.redirtbl[i].bits = state->redirtbl[i]; + } @@ -3769,28 +3527,18 @@ index fa73141590..42c44765e0 100644 + int rcLnx = ioctl(pVM->nem.s.fdVm, KVM_SET_IRQCHIP, &irqchip_state); + AssertLogRelMsgReturn(rcLnx == 0, ("NEMR3KvmSetIoApicState: \ + Failed to set IOPIC state. Error: %d, errno %d\n", rcLnx, errno), VERR_NEM_IPE_5); - - return VINF_SUCCESS; - } ++ ++ return VINF_SUCCESS; ++} +#endif - -- --VMMR3_INT_DECL(int) NEMR3NotifyPhysRomRegisterEarly(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, void *pvPages, uint32_t fFlags, -- uint8_t *pu2State, uint32_t *puNemRange) ++ +static int kvmSetGsiRouting(PVM pVM) - { -- Log5(("NEMR3NotifyPhysRomRegisterEarly: %RGp LB %RGp pvPages=%p fFlags=%#x\n", GCPhys, cb, pvPages, fFlags)); -- *pu2State = UINT8_MAX; ++{ + alignas(kvm_irq_routing) char backing[ sizeof(struct kvm_irq_routing) + KVM_IRQCHIP_NUM_IOAPIC_INTR_PINS * sizeof(struct kvm_irq_routing_entry) ] {}; + kvm_irq_routing* routing = reinterpret_cast(backing); - -- /* Don't support puttint ROM where there is already RAM. For -- now just shuffle the registrations till it works... */ -- AssertLogRelMsgReturn(!(fFlags & NEM_NOTIFY_PHYS_ROM_F_REPLACE), ("%RGp LB %RGp fFlags=%#x\n", GCPhys, cb, fFlags), -- VERR_NEM_MAP_PAGES_FAILED); ++ + unsigned routingCount {0}; - -- /** @todo figure out how to do shadow ROMs. */ ++ + for(unsigned i {0}; i < KVM_IRQCHIP_NUM_IOAPIC_INTR_PINS; ++i) + { + if (pVM->nem.s.pARedirectionTable->at(i).has_value()) @@ -3804,82 +3552,43 @@ index fa73141590..42c44765e0 100644 + routingCount++; + } + } - -- /* -- * We only allocate a slot number here in case we need to use it to -- * fend of physical handler fun. -- */ -- uint16_t idSlot = nemR3LnxMemSlotIdAlloc(pVM); -- AssertLogRelReturn(idSlot < _32K, VERR_NEM_MAP_PAGES_FAILED); ++ + routing->nr = routingCount; + + int rc = ioctl(pVM->nem.s.fdVm, KVM_SET_GSI_ROUTING, routing); + + AssertLogRelMsgReturn(rc >= 0, ("NEM/KVM: Unable to set GSI routing! rc: %d errno %d \n", rc, errno), VERR_INTERNAL_ERROR); - -- *pu2State = 0; -- *puNemRange = idSlot; -- Log5(("NEMR3NotifyPhysRomRegisterEarly: %RGp LB %RGp fFlags=%#x pvPages=%p - idSlot=%#x\n", -- GCPhys, cb, fFlags, pvPages, idSlot)); -- RT_NOREF(GCPhys, cb, fFlags, pvPages); - return VINF_SUCCESS; - } - - --VMMR3_INT_DECL(int) NEMR3NotifyPhysRomRegisterLate(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cb, void *pvPages, -- uint32_t fFlags, uint8_t *pu2State, uint32_t *puNemRange) ++ ++ return VINF_SUCCESS; ++} ++ ++ +VMMR3_INT_DECL(int) NEMR3KvmSplitIrqchipAddUpdateRTE(PVM pVM, uint16_t u16Gsi, PCMSIMSG pMsi) - { -- Log5(("NEMR3NotifyPhysRomRegisterLate: %RGp LB %RGp pvPages=%p fFlags=%#x pu2State=%p (%d) puNemRange=%p (%#x)\n", -- GCPhys, cb, pvPages, fFlags, pu2State, *pu2State, puNemRange, *puNemRange)); ++{ + AssertRelease(pVM->nem.s.pARedirectionTable != nullptr); + AssertRelease(u16Gsi < KVM_IRQCHIP_NUM_IOAPIC_INTR_PINS); - -- AssertPtrReturn(pvPages, VERR_NEM_IPE_5); ++ + pVM->nem.s.pARedirectionTable->at(u16Gsi) = *pMsi; - -- uint32_t const idSlot = *puNemRange; -- AssertReturn(idSlot > 0 && idSlot < _32K, VERR_NEM_IPE_4); -- AssertReturn(ASMBitTest(pVM->nem.s.bmSlotIds, idSlot), VERR_NEM_IPE_4); ++ + return kvmSetGsiRouting(pVM); +} - -- *pu2State = UINT8_MAX; - -- /* -- * Do the actual setting of the user pages here now that we've -- * got a valid pvPages (typically isn't available during the early -- * notification, unless we're replacing RAM). -- */ -- struct kvm_userspace_memory_region Region; -- Region.slot = idSlot; -- Region.flags = 0; -- Region.guest_phys_addr = GCPhys; -- Region.memory_size = cb; -- Region.userspace_addr = (uintptr_t)pvPages; ++ ++ +VMMR3_INT_DECL(int) NEMR3KvmSplitIrqchipRemoveRTE(PVM pVM, uint16_t u16Gsi) +{ + AssertRelease(pVM->nem.s.pARedirectionTable != nullptr); + AssertRelease(u16Gsi < KVM_IRQCHIP_NUM_IOAPIC_INTR_PINS); - -- int rc = ioctl(pVM->nem.s.fdVm, KVM_SET_USER_MEMORY_REGION, &Region); -- if (rc == 0) -- { -- *pu2State = 0; -- Log5(("NEMR3NotifyPhysRomRegisterEarly: %RGp LB %RGp fFlags=%#x pvPages=%p - idSlot=%#x\n", -- GCPhys, cb, fFlags, pvPages, idSlot)); -- return VINF_SUCCESS; -- } -- AssertLogRelMsgFailedReturn(("%RGp LB %RGp fFlags=%#x, pvPages=%p, idSlot=%#x failed: %u/%u\n", -- GCPhys, cb, fFlags, pvPages, idSlot, errno, rc), -- VERR_NEM_MAP_PAGES_FAILED); ++ + pVM->nem.s.pARedirectionTable->at(u16Gsi) = std::nullopt; + + return kvmSetGsiRouting(pVM); - } - - -@@ -1329,8 +2201,7 @@ static int nemHCLnxImportState(PVMCPUCC pVCpu, uint64_t fWhat, PCPUMCTX pCtx, st ++} ++ ++ + /********************************************************************************************************************************* + * CPU State * + *********************************************************************************************************************************/ +@@ -386,8 +1167,7 @@ static int nemHCLnxImportState(PVMCPUCC pVCpu, uint64_t fWhat, PCPUMCTX pCtx, st } } } @@ -3889,7 +3598,7 @@ index fa73141590..42c44765e0 100644 if (fWhat & CPUMCTX_EXTRN_EFER) { if (pCtx->msrEFER != pRun->s.regs.sregs.efer) -@@ -1397,6 +2268,7 @@ static int nemHCLnxImportState(PVMCPUCC pVCpu, uint64_t fWhat, PCPUMCTX pCtx, st +@@ -454,6 +1234,7 @@ static int nemHCLnxImportState(PVMCPUCC pVCpu, uint64_t fWhat, PCPUMCTX pCtx, st pCtx->aXcr[0] = Xcrs.xcrs[0].value; pCtx->aXcr[1] = Xcrs.xcrs[1].value; @@ -3897,7 +3606,7 @@ index fa73141590..42c44765e0 100644 } } -@@ -1444,6 +2316,8 @@ static int nemHCLnxImportState(PVMCPUCC pVCpu, uint64_t fWhat, PCPUMCTX pCtx, st +@@ -501,6 +1282,8 @@ static int nemHCLnxImportState(PVMCPUCC pVCpu, uint64_t fWhat, PCPUMCTX pCtx, st if (fWhat & CPUMCTX_EXTRN_OTHER_MSRS) { ADD_MSR(MSR_IA32_CR_PAT, pCtx->msrPAT); @@ -3906,7 +3615,7 @@ index fa73141590..42c44765e0 100644 /** @todo What do we _have_ to add here? * We also have: Mttr*, MiscEnable, FeatureControl. */ } -@@ -1481,12 +2355,6 @@ static int nemHCLnxImportState(PVMCPUCC pVCpu, uint64_t fWhat, PCPUMCTX pCtx, st +@@ -538,12 +1321,6 @@ static int nemHCLnxImportState(PVMCPUCC pVCpu, uint64_t fWhat, PCPUMCTX pCtx, st pVCpu->cpum.GstCtx.rip); CPUMUpdateInterruptInhibitingByNmi(&pVCpu->cpum.GstCtx, KvmEvents.nmi.masked != 0); @@ -3919,7 +3628,7 @@ index fa73141590..42c44765e0 100644 Assert(KvmEvents.nmi.injected == 0); Assert(KvmEvents.nmi.pending == 0); } -@@ -1597,6 +2465,13 @@ VMM_INT_DECL(int) NEMImportStateOnDemand(PVMCPUCC pVCpu, uint64_t fWhat) +@@ -654,6 +1431,13 @@ VMM_INT_DECL(int) NEMImportStateOnDemand(PVMCPUCC pVCpu, uint64_t fWhat) */ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_run *pRun) { @@ -3933,7 +3642,7 @@ index fa73141590..42c44765e0 100644 uint64_t const fExtrn = ~pCtx->fExtrn & CPUMCTX_EXTRN_ALL; Assert((~fExtrn & CPUMCTX_EXTRN_ALL) != CPUMCTX_EXTRN_ALL); -@@ -1605,39 +2480,53 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ +@@ -662,39 +1446,53 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ */ if (fExtrn & (CPUMCTX_EXTRN_RIP | CPUMCTX_EXTRN_RFLAGS | CPUMCTX_EXTRN_GPRS_MASK)) { @@ -4001,6 +3710,8 @@ index fa73141590..42c44765e0 100644 - pRun->s.regs.regs.r13 = pCtx->r13; - pRun->s.regs.regs.r14 = pCtx->r14; - pRun->s.regs.regs.r15 = pCtx->r15; +- } +- pRun->kvm_dirty_regs |= KVM_SYNC_X86_REGS; + NEM_UPDATE_IF_CHANGED(pRun->s.regs.regs.r8, pCtx->r8, dirty_gprs); + NEM_UPDATE_IF_CHANGED(pRun->s.regs.regs.r9, pCtx->r9, dirty_gprs); + NEM_UPDATE_IF_CHANGED(pRun->s.regs.regs.r10, pCtx->r10, dirty_gprs); @@ -4012,12 +3723,11 @@ index fa73141590..42c44765e0 100644 + } + if (dirty_gprs) { + pRun->kvm_dirty_regs |= KVM_SYNC_X86_REGS; - } -- pRun->kvm_dirty_regs |= KVM_SYNC_X86_REGS; ++ } } /* -@@ -1651,15 +2540,7 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ +@@ -708,15 +1506,7 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ | CPUMCTX_EXTRN_EFER | CPUMCTX_EXTRN_APIC_TPR)) || uApicBase != pVCpu->nem.s.uKvmApicBase) { @@ -4034,7 +3744,7 @@ index fa73141590..42c44765e0 100644 (a_KvmSeg).base = (a_CtxSeg).u64Base; \ (a_KvmSeg).limit = (a_CtxSeg).u32Limit; \ (a_KvmSeg).selector = (a_CtxSeg).Sel; \ -@@ -1673,64 +2554,123 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ +@@ -730,64 +1520,123 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ (a_KvmSeg).g = (a_CtxSeg).Attr.n.u1Granularity; \ (a_KvmSeg).unusable = (a_CtxSeg).Attr.n.u1Unusable; \ (a_KvmSeg).padding = 0; \ @@ -4152,16 +3862,15 @@ index fa73141590..42c44765e0 100644 + if (fExtrn & CPUMCTX_EXTRN_CR4) { + NEM_UPDATE_IF_CHANGED(pRun->s.regs.sregs.cr4, pCtx->cr4, dirty_sregs); + } -+ } -+ if (fExtrn & CPUMCTX_EXTRN_EFER) { -+ NEM_UPDATE_IF_CHANGED(pRun->s.regs.sregs.efer, pCtx->msrEFER, dirty_sregs); } - if (fExtrn & CPUMCTX_EXTRN_EFER) - pRun->s.regs.sregs.efer = pCtx->msrEFER; ++ if (fExtrn & CPUMCTX_EXTRN_EFER) { ++ NEM_UPDATE_IF_CHANGED(pRun->s.regs.sregs.efer, pCtx->msrEFER, dirty_sregs); ++ } ++ - RT_ZERO(pRun->s.regs.sregs.interrupt_bitmap); /* this is an alternative interrupt injection interface */ - -- pRun->kvm_dirty_regs |= KVM_SYNC_X86_SREGS; + if (dirty_sregs) { + pRun->kvm_dirty_regs |= KVM_SYNC_X86_SREGS; + } else { @@ -4182,7 +3891,8 @@ index fa73141590..42c44765e0 100644 + // where this field is no longer present. + RT_ZERO(pRun->s.regs.sregs.interrupt_bitmap); + } -+ + +- pRun->kvm_dirty_regs |= KVM_SYNC_X86_SREGS; } +#undef NEM_LNX_EXPORT_SEG +#undef NEM_LNX_SREG_IDENTICAL @@ -4190,7 +3900,7 @@ index fa73141590..42c44765e0 100644 /* * Debug registers. -@@ -1836,6 +2776,8 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ +@@ -893,6 +1742,8 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ if (fExtrn & CPUMCTX_EXTRN_OTHER_MSRS) { ADD_MSR(MSR_IA32_CR_PAT, pCtx->msrPAT); @@ -4199,7 +3909,7 @@ index fa73141590..42c44765e0 100644 /** @todo What do we _have_ to add here? * We also have: Mttr*, MiscEnable, FeatureControl. */ } -@@ -1862,37 +2804,20 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ +@@ -919,6 +1770,8 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ == (CPUMCTX_EXTRN_INHIBIT_INT | CPUMCTX_EXTRN_INHIBIT_NMI)); struct kvm_vcpu_events KvmEvents = {0}; @@ -4208,13 +3918,7 @@ index fa73141590..42c44765e0 100644 KvmEvents.flags = KVM_VCPUEVENT_VALID_SHADOW; if (!CPUMIsInInterruptShadowWithUpdate(&pVCpu->cpum.GstCtx)) - { /* probably likely */ } - else -- KvmEvents.interrupt.shadow = (CPUMIsInInterruptShadowAfterSs() ? KVM_X86_SHADOW_INT_MOV_SS : 0) -- | (CPUMIsInInterruptShadowAfterSti() ? KVM_X86_SHADOW_INT_STI : 0); -+ KvmEvents.interrupt.shadow = (CPUMIsInInterruptShadowAfterSs(&pVCpu->cpum.GstCtx) ? KVM_X86_SHADOW_INT_MOV_SS : 0) -+ | (CPUMIsInInterruptShadowAfterSti(&pVCpu->cpum.GstCtx) ? KVM_X86_SHADOW_INT_STI : 0); - +@@ -930,26 +1783,7 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ /* No flag - this is updated unconditionally. */ KvmEvents.nmi.masked = CPUMAreInterruptsInhibitedByNmi(&pVCpu->cpum.GstCtx); @@ -4242,7 +3946,7 @@ index fa73141590..42c44765e0 100644 AssertLogRelMsgReturn(rcLnx == 0, ("rcLnx=%d errno=%d\n", rcLnx, errno), VERR_NEM_IPE_3); } -@@ -1917,8 +2842,31 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ +@@ -974,8 +1808,31 @@ static int nemHCLnxExportState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, struct kvm_ VMM_INT_DECL(int) NEMHCQueryCpuTick(PVMCPUCC pVCpu, uint64_t *pcTicks, uint32_t *puAux) { STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatQueryCpuTick); @@ -4276,7 +3980,7 @@ index fa73141590..42c44765e0 100644 return VINF_SUCCESS; } -@@ -1935,8 +2883,39 @@ VMM_INT_DECL(int) NEMHCQueryCpuTick(PVMCPUCC pVCpu, uint64_t *pcTicks, uint32_t +@@ -992,8 +1849,39 @@ VMM_INT_DECL(int) NEMHCQueryCpuTick(PVMCPUCC pVCpu, uint64_t *pcTicks, uint32_t */ VMM_INT_DECL(int) NEMHCResumeCpuTickOnAll(PVMCC pVM, PVMCPUCC pVCpu, uint64_t uPausedTscValue) { @@ -4318,7 +4022,7 @@ index fa73141590..42c44765e0 100644 return VINF_SUCCESS; } -@@ -1958,6 +2937,7 @@ VMM_INT_DECL(uint32_t) NEMHCGetFeatures(PVMCC pVM) +@@ -1015,6 +1903,7 @@ VMM_INT_DECL(uint32_t) NEMHCGetFeatures(PVMCC pVM) VMMR3_INT_DECL(bool) NEMR3CanExecuteGuest(PVM pVM, PVMCPU pVCpu) { @@ -4326,7 +4030,7 @@ index fa73141590..42c44765e0 100644 /* * Only execute when the A20 gate is enabled as I cannot immediately * spot any A20 support in KVM. -@@ -1965,6 +2945,15 @@ VMMR3_INT_DECL(bool) NEMR3CanExecuteGuest(PVM pVM, PVMCPU pVCpu) +@@ -1022,6 +1911,15 @@ VMMR3_INT_DECL(bool) NEMR3CanExecuteGuest(PVM pVM, PVMCPU pVCpu) RT_NOREF(pVM); Assert(VM_IS_NEM_ENABLED(pVM)); return PGMPhysIsA20Enabled(pVCpu); @@ -4342,7 +4046,7 @@ index fa73141590..42c44765e0 100644 } -@@ -1977,6 +2966,14 @@ bool nemR3NativeSetSingleInstruction(PVM pVM, PVMCPU pVCpu, bool fEnable) +@@ -1034,6 +1932,14 @@ bool nemR3NativeSetSingleInstruction(PVM pVM, PVMCPU pVCpu, bool fEnable) void nemR3NativeNotifyFF(PVM pVM, PVMCPU pVCpu, uint32_t fFlags) { @@ -4357,7 +4061,7 @@ index fa73141590..42c44765e0 100644 int rc = RTThreadPoke(pVCpu->hThread); LogFlow(("nemR3NativeNotifyFF: #%u -> %Rrc\n", pVCpu->idCpu, rc)); AssertRC(rc); -@@ -2010,12 +3007,10 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ +@@ -1067,12 +1973,10 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ * only inject one event per KVM_RUN call. This can only happend if we * can directly from the loop in EM, so the inhibit bits must be internal. */ @@ -4372,7 +4076,7 @@ index fa73141590..42c44765e0 100644 return VINF_SUCCESS; } -@@ -2024,12 +3019,12 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ +@@ -1081,12 +1985,12 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ */ if (VMCPU_FF_TEST_AND_CLEAR(pVCpu, VMCPU_FF_UPDATE_APIC)) { @@ -4389,23 +4093,7 @@ index fa73141590..42c44765e0 100644 /* * We don't currently implement SMIs. */ -@@ -2052,12 +3047,12 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ - KvmEvents.flags |= KVM_VCPUEVENT_VALID_SHADOW; - if (!(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_INHIBIT_INT)) - KvmEvents.interrupt.shadow = !CPUMIsInInterruptShadowWithUpdate(&pVCpu->cpum.GstCtx) ? 0 -- : (CPUMIsInInterruptShadowAfterSs() ? KVM_X86_SHADOW_INT_MOV_SS : 0) -- | (CPUMIsInInterruptShadowAfterSti() ? KVM_X86_SHADOW_INT_STI : 0); -+ : (CPUMIsInInterruptShadowAfterSs(&pVCpu->cpum.GstCtx) ? KVM_X86_SHADOW_INT_MOV_SS : 0) -+ | (CPUMIsInInterruptShadowAfterSti(&pVCpu->cpum.GstCtx) ? KVM_X86_SHADOW_INT_STI : 0); - else - CPUMUpdateInterruptShadowSsStiEx(&pVCpu->cpum.GstCtx, - RT_BOOL(KvmEvents.interrupt.shadow & KVM_X86_SHADOW_INT_MOV_SS), -- RT_BOOL(KvmEvents.interrupt.shadow & KVM_X86_SHADOW_INT_MOV_STI), -+ RT_BOOL(KvmEvents.interrupt.shadow & KVM_X86_SHADOW_INT_STI), - pRun->s.regs.regs.rip); - - if (!(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_INHIBIT_NMI)) -@@ -2085,35 +3080,24 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ +@@ -1142,35 +2046,24 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ Log8(("Queuing NMI on %u\n", pVCpu->idCpu)); } @@ -4449,7 +4137,7 @@ index fa73141590..42c44765e0 100644 Log8(("Queuing interrupt %#x on %u: %04x:%08RX64 efl=%#x\n", bInterrupt, pVCpu->idCpu, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.eflags.u)); } -@@ -2134,7 +3118,7 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ +@@ -1191,7 +2084,7 @@ static VBOXSTRICTRC nemHCLnxHandleInterruptFF(PVM pVM, PVMCPU pVCpu, struct kvm_ Log8(("Interrupt window pending on %u (#1)\n", pVCpu->idCpu)); } } @@ -4458,7 +4146,7 @@ index fa73141590..42c44765e0 100644 /* * Now, update the state. */ -@@ -2321,6 +3305,16 @@ static VBOXSTRICTRC nemHCLnxHandleExitMmio(PVMCC pVM, PVMCPUCC pVCpu, struct kvm +@@ -1378,6 +2271,16 @@ static VBOXSTRICTRC nemHCLnxHandleExitMmio(PVMCC pVM, PVMCPUCC pVCpu, struct kvm VBOXSTRICTRC rcStrict; if (pRun->mmio.is_write) { @@ -4475,7 +4163,7 @@ index fa73141590..42c44765e0 100644 rcStrict = PGMPhysWrite(pVM, pRun->mmio.phys_addr, pRun->mmio.data, pRun->mmio.len, PGMACCESSORIGIN_HM); Log4(("MmioExit/%u: %04x:%08RX64: WRITE %#x LB %u, %.*Rhxs -> rcStrict=%Rrc\n", pVCpu->idCpu, pRun->s.regs.sregs.cs.selector, pRun->s.regs.regs.rip, -@@ -2420,8 +3414,6 @@ static VBOXSTRICTRC nemHCLnxHandleExitWrMsr(PVMCPUCC pVCpu, struct kvm_run *pRun +@@ -1477,8 +2380,6 @@ static VBOXSTRICTRC nemHCLnxHandleExitWrMsr(PVMCPUCC pVCpu, struct kvm_run *pRun return rcStrict; } @@ -4484,7 +4172,7 @@ index fa73141590..42c44765e0 100644 static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run *pRun, bool *pfStatefulExit) { STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitTotal); -@@ -2450,12 +3442,10 @@ static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run +@@ -1507,12 +2408,10 @@ static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run return VINF_SUCCESS; case KVM_EXIT_SET_TPR: @@ -4497,7 +4185,7 @@ index fa73141590..42c44765e0 100644 AssertFailed(); break; -@@ -2481,6 +3471,10 @@ static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run +@@ -1538,6 +2437,10 @@ static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run pRun->s.regs.regs.rip + pRun->s.regs.sregs.cs.base, ASMReadTSC()); STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitIntr); Log5(("Intr/%u\n", pVCpu->idCpu)); @@ -4508,7 +4196,7 @@ index fa73141590..42c44765e0 100644 return VINF_SUCCESS; case KVM_EXIT_HYPERCALL: -@@ -2497,11 +3491,48 @@ static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run +@@ -1554,11 +2457,48 @@ static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run AssertFailed(); break; case KVM_EXIT_IOAPIC_EOI: @@ -4561,7 +4249,7 @@ index fa73141590..42c44765e0 100644 case KVM_EXIT_DIRTY_RING_FULL: AssertFailed(); -@@ -2569,6 +3600,82 @@ static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run +@@ -1626,6 +2566,82 @@ static VBOXSTRICTRC nemHCLnxHandleExit(PVMCC pVM, PVMCPUCC pVCpu, struct kvm_run return VERR_NOT_IMPLEMENTED; } @@ -4644,7 +4332,7 @@ index fa73141590..42c44765e0 100644 VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) { -@@ -2584,6 +3691,28 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) +@@ -1641,6 +2657,28 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) return VINF_SUCCESS; } @@ -4673,7 +4361,7 @@ index fa73141590..42c44765e0 100644 /* * The run loop. */ -@@ -2612,6 +3741,8 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) +@@ -1669,6 +2707,8 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) } } @@ -4682,7 +4370,7 @@ index fa73141590..42c44765e0 100644 /* * Do not execute in KVM if the A20 isn't enabled. */ -@@ -2623,6 +3754,7 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) +@@ -1680,6 +2720,7 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) LogFlow(("NEM/%u: breaking: A20 disabled\n", pVCpu->idCpu)); break; } @@ -4690,7 +4378,7 @@ index fa73141590..42c44765e0 100644 /* * Ensure KVM has the whole state. -@@ -2633,17 +3765,9 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) +@@ -1690,17 +2731,9 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) AssertRCReturn(rc2, rc2); } @@ -4711,7 +4399,7 @@ index fa73141590..42c44765e0 100644 if ( !VM_FF_IS_ANY_SET(pVM, VM_FF_EMT_RENDEZVOUS | VM_FF_TM_VIRTUAL_SYNC) && !VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_HM_TO_R3_MASK)) { -@@ -2653,13 +3777,25 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) +@@ -1710,13 +2743,25 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) pVCpu->idCpu, pRun->s.regs.sregs.cs.selector, pRun->s.regs.regs.rip, !!(pRun->s.regs.regs.rflags & X86_EFL_IF), pRun->s.regs.regs.rflags, pRun->s.regs.sregs.ss.selector, pRun->s.regs.regs.rsp, pRun->s.regs.sregs.cr0)); @@ -4737,7 +4425,7 @@ index fa73141590..42c44765e0 100644 #ifdef LOG_ENABLED if (LogIsFlowEnabled()) { -@@ -2672,8 +3808,15 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) +@@ -1729,8 +2774,15 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) } #endif fStatefulExit = false; @@ -4754,7 +4442,7 @@ index fa73141590..42c44765e0 100644 /* * Deal with the exit. */ -@@ -2687,10 +3830,19 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) +@@ -1744,10 +2796,19 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) break; } } @@ -4776,24 +4464,182 @@ index fa73141590..42c44765e0 100644 } /* -@@ -2835,4 +3987,3 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) +@@ -1892,4 +2953,3 @@ VBOXSTRICTRC nemR3NativeRunGC(PVM pVM, PVMCPU pVCpu) * This is using KVM. * */ - +diff --git a/src/VBox/VMM/VMMR3/NEMR3NativeTemplate-linux.cpp.h b/src/VBox/VMM/VMMR3/NEMR3NativeTemplate-linux.cpp.h +index 39a9d3b56a..bbe5976334 100644 +--- a/src/VBox/VMM/VMMR3/NEMR3NativeTemplate-linux.cpp.h ++++ b/src/VBox/VMM/VMMR3/NEMR3NativeTemplate-linux.cpp.h +@@ -420,6 +420,7 @@ static int nemR3LnxInitCheckCapabilities(PVM pVM, PRTERRINFO pErrInfo) + + + /** @callback_method_impl{FNVMMEMTRENDEZVOUS} */ ++#ifndef VBOX_WITH_KVM + static DECLCALLBACK(VBOXSTRICTRC) nemR3LnxFixThreadPoke(PVM pVM, PVMCPU pVCpu, void *pvUser) + { + RT_NOREF(pVM, pvUser); +@@ -427,8 +428,98 @@ static DECLCALLBACK(VBOXSTRICTRC) nemR3LnxFixThreadPoke(PVM pVM, PVMCPU pVCpu, v + AssertLogRelRC(rc); + return VINF_SUCCESS; + } ++#else ++static VBOXSTRICTRC nemR3LnxSetVCpuSignalMask(PVMCPU pVCpu, sigset_t *pSigset) ++{ ++ /* ++ * glibc and Linux/KVM do not agree on the size of sigset_t. ++ */ ++ constexpr size_t kernel_sigset_size = 8; ++ ++ alignas(kvm_signal_mask) char backing[sizeof(kvm_signal_mask) + kernel_sigset_size]; ++ kvm_signal_mask *pKvmSignalMask = reinterpret_cast(backing); ++ ++ static_assert(sizeof(sigset_t) >= kernel_sigset_size); ++ ++ pKvmSignalMask->len = kernel_sigset_size; ++ memcpy(pKvmSignalMask->sigset, pSigset, kernel_sigset_size); ++ ++ int rc = ioctl(pVCpu->nem.s.fdVCpu, KVM_SET_SIGNAL_MASK, pKvmSignalMask); ++ AssertLogRelMsgReturn(rc == 0, ("Failed to set vCPU signal mask: %d", errno), ++ VERR_NEM_INIT_FAILED); ++ ++ return VINF_SUCCESS; ++} ++ ++static DECLCALLBACK(VBOXSTRICTRC) nemR3LnxFixThreadPoke(PVM pVM, PVMCPU pVCpu, void *pvUser) ++{ ++ RT_NOREF(pVM, pvUser); ++ ++ int iPokeSignal = RTThreadPokeSignal(); ++ AssertReturn(iPokeSignal >= 0, VERR_NEM_INIT_FAILED); ++ ++ /* We disable the poke signal for the host. We never want that signal to be delivered. */ ++ int rc = RTThreadControlPokeSignal(pVCpu->hThread, false /*fEnable*/); ++ AssertLogRelRC(rc); ++ ++ sigset_t sigset; ++ ++ /* Fetch the current signal mask. */ ++ int rcProcMask = pthread_sigmask(SIG_BLOCK /* ignored */, nullptr, &sigset); ++ AssertLogRelMsgReturn(rcProcMask == 0, ("Failed to retrieve thread signal mask"), VERR_NEM_INIT_FAILED); ++ ++ sigdelset(&sigset, iPokeSignal); ++ ++ /* We enable the poke signal for the vCPU. Any poke will kick the vCPU out of guest execution. */ ++ VBOXSTRICTRC rcVcpuMask = nemR3LnxSetVCpuSignalMask(pVCpu, &sigset); ++ AssertRCSuccessReturn(rcVcpuMask, rcVcpuMask); ++ ++ /* Create a timer that delivers the poke signal. */ ++ struct sigevent sev {}; ++ ++ sev.sigev_notify = SIGEV_THREAD_ID; ++ sev.sigev_signo = iPokeSignal; ++ sev._sigev_un._tid = gettid(); ++ ++ int rcTimer = timer_create(CLOCK_MONOTONIC, &sev, &pVCpu->nem.s.pTimer); ++ AssertLogRelMsgReturn(rcTimer == 0, ("Failed to create timer: %d", errno), VERR_NEM_INIT_FAILED); ++ ++ return VINF_SUCCESS; ++} ++#endif + + ++#ifdef VBOX_WITH_KVM ++/** ++ * Check common environment problems and inform the user about misconfigurations. ++ */ ++int nemR3CheckEnvironment(void) ++{ ++ static const char szSplitLockMitigationFile[] = "/proc/sys/kernel/split_lock_mitigate"; ++ ++ char buf[64] {}; ++ int fd = open(szSplitLockMitigationFile, O_RDONLY | O_CLOEXEC); ++ ++ // Older kernels might not have this. A hard error feels unjustified here. ++ AssertLogRelMsgReturn(fd >= 0, ("Failed to check %s (%d). Assuming there is no problem.\n", szSplitLockMitigationFile, fd), ++ VINF_SUCCESS); ++ ++ /* Leave one character to ensure that the string is zero-terminated. */ ++ ssize_t bytes = read(fd, buf, sizeof(buf) - 1); ++ AssertLogRelMsgReturn(bytes >= 0, ("Failed to read %s (%zd)\n", szSplitLockMitigationFile, bytes), ++ VERR_NEM_INIT_FAILED); ++ ++ int mitigationStatus = atoi(buf); ++ ++ if (mitigationStatus != 0) { ++ LogRel(("NEM: WARNING: %s is %d. This can cause VM hangs, unless you set split_lock_detect=off on the host kernel command line! Please set it to 0.\n", ++ szSplitLockMitigationFile, mitigationStatus)); ++ } ++ ++ return VINF_SUCCESS; ++} ++#endif ++ + /** + * Try initialize the native API. + * +@@ -445,6 +536,12 @@ static DECLCALLBACK(VBOXSTRICTRC) nemR3LnxFixThreadPoke(PVM pVM, PVMCPU pVCpu, v + int nemR3NativeInit(PVM pVM, bool fFallback, bool fForced) + { + RT_NOREF(pVM, fFallback, fForced); ++ ++#ifdef VBOX_WITH_KVM ++ int rcCheck = nemR3CheckEnvironment(); ++ AssertLogRelMsgReturn(RT_SUCCESS(rcCheck), ("Failed to check environment\n"), VERR_NEM_INIT_FAILED); ++#endif ++ + /* + * Some state init. + */ +@@ -631,6 +728,10 @@ int nemR3NativeTerm(PVM pVM) + close(pVM->nem.s.fdKvm); + pVM->nem.s.fdKvm = -1; + } ++ ++#ifdef VBOX_WITH_KVM ++ pVM->nem.s.pARedirectionTable.reset(); ++#endif + return VINF_SUCCESS; + } + +@@ -642,7 +743,22 @@ int nemR3NativeTerm(PVM pVM) + */ + void nemR3NativeReset(PVM pVM) + { ++#ifndef VBOX_WITH_KVM + RT_NOREF(pVM); ++#else ++ pVM->nem.s.pARedirectionTable->fill(std::nullopt); ++ ++ for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) ++ { ++ PVMCPU pVCpu = pVM->apCpusR3[idCpu]; ++ ++ struct kvm_mp_state mp; ++ mp.mp_state = pVCpu->idCpu == 0 ? KVM_MP_STATE_RUNNABLE : KVM_MP_STATE_UNINITIALIZED; ++ ++ int rcLnx = ioctl(pVCpu->nem.s.fdVCpu, KVM_SET_MP_STATE, &mp); ++ AssertLogRelMsg(rcLnx == 0, ("nemR3NativeReset: Failed to set MP state. Error: %d, errno %d\n", rcLnx, errno)); ++ } ++#endif + } + + diff --git a/src/VBox/VMM/VMMR3/PDMDevMiscHlp.cpp b/src/VBox/VMM/VMMR3/PDMDevMiscHlp.cpp -index f11f0b2151..d723f54531 100644 +index ca7f32b5ea..a871a82573 100644 --- a/src/VBox/VMM/VMMR3/PDMDevMiscHlp.cpp +++ b/src/VBox/VMM/VMMR3/PDMDevMiscHlp.cpp -@@ -34,6 +34,7 @@ - #include - #include - #include +@@ -37,6 +37,7 @@ + #ifndef VBOX_VMM_TARGET_ARMV8 + # include + #endif +#include - #include #include #include -@@ -98,6 +99,34 @@ static DECLCALLBACK(void) pdmR3PicHlp_Unlock(PPDMDEVINS pDevIns) + +@@ -110,6 +111,34 @@ static DECLCALLBACK(void) pdmR3PicHlp_Unlock(PPDMDEVINS pDevIns) pdmUnlock(pDevIns->Internal.s.pVMR3); } @@ -4828,7 +4674,7 @@ index f11f0b2151..d723f54531 100644 /** * PIC Device Helpers. -@@ -109,6 +138,11 @@ const PDMPICHLP g_pdmR3DevPicHlp = +@@ -121,6 +150,11 @@ const PDMPICHLP g_pdmR3DevPicHlp = pdmR3PicHlp_ClearInterruptFF, pdmR3PicHlp_Lock, pdmR3PicHlp_Unlock, @@ -4840,7 +4686,7 @@ index f11f0b2151..d723f54531 100644 PDM_PICHLP_VERSION /* the end */ }; -@@ -175,7 +209,64 @@ static DECLCALLBACK(int) pdmR3IoApicHlp_IommuMsiRemap(PPDMDEVINS pDevIns, uint16 +@@ -193,7 +227,64 @@ static DECLCALLBACK(int) pdmR3IoApicHlp_IommuMsiRemap(PPDMDEVINS pDevIns, uint16 return VERR_IOMMU_NOT_PRESENT; } @@ -4905,7 +4751,7 @@ index f11f0b2151..d723f54531 100644 /** * I/O APIC Device Helpers. */ -@@ -187,6 +278,17 @@ const PDMIOAPICHLP g_pdmR3DevIoApicHlp = +@@ -205,6 +296,17 @@ const PDMIOAPICHLP g_pdmR3DevIoApicHlp = pdmR3IoApicHlp_Unlock, pdmR3IoApicHlp_LockIsOwner, pdmR3IoApicHlp_IommuMsiRemap, @@ -4924,10 +4770,10 @@ index f11f0b2151..d723f54531 100644 }; diff --git a/src/VBox/VMM/VMMR3/PGMPhys.cpp b/src/VBox/VMM/VMMR3/PGMPhys.cpp -index fb9fd66828..b54be52089 100644 +index a868cd1537..58059517e3 100644 --- a/src/VBox/VMM/VMMR3/PGMPhys.cpp +++ b/src/VBox/VMM/VMMR3/PGMPhys.cpp -@@ -1862,7 +1862,12 @@ int pgmR3PhysRamPreAllocate(PVM pVM) +@@ -2106,7 +2106,12 @@ int pgmR3PhysRamPreAllocate(PVM pVM) Assert(pVM->pgm.s.fRamPreAlloc); Log(("pgmR3PhysRamPreAllocate: enter\n")); #ifdef VBOX_WITH_PGM_NEM_MODE @@ -4941,10 +4787,10 @@ index fb9fd66828..b54be52089 100644 /* diff --git a/src/VBox/VMM/VMMR3/VMM.cpp b/src/VBox/VMM/VMMR3/VMM.cpp -index e235184c56..787df961f1 100644 +index 8fc8f20faf..aea309f6d9 100644 --- a/src/VBox/VMM/VMMR3/VMM.cpp +++ b/src/VBox/VMM/VMMR3/VMM.cpp -@@ -1092,6 +1092,11 @@ static DECLCALLBACK(int) vmmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, +@@ -1101,6 +1101,11 @@ static DECLCALLBACK(int) vmmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, AssertMsgFailed(("u32=%#x\n", u32)); return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; } @@ -4957,7 +4803,7 @@ index e235184c56..787df961f1 100644 } diff --git a/src/VBox/VMM/include/GIMHvInternal.h b/src/VBox/VMM/include/GIMHvInternal.h -index 960dc36c6c..7308180cb5 100644 +index 29522e02be..9216ebb1d4 100644 --- a/src/VBox/VMM/include/GIMHvInternal.h +++ b/src/VBox/VMM/include/GIMHvInternal.h @@ -202,6 +202,8 @@ @@ -4969,19 +4815,27 @@ index 960dc36c6c..7308180cb5 100644 /** @} */ -@@ -1100,6 +1102,7 @@ AssertCompileSize(GIMHVEXTGETBOOTZEROMEM, 16); +@@ -1117,6 +1119,15 @@ AssertCompile(sizeof(GIMHVEXTGETBOOTZEROMEM) <= GIM_HV_PAGE_SIZE); + /** @} */ - /** Microsoft Hyper-V vendor signature. */ - #define GIM_HV_VENDOR_MICROSOFT "Microsoft Hv" -+#define GIM_HV_VENDOR_VBOX "VBoxVBoxVBox" ++/** Hyper-V page size. */ ++#define GIM_HV_PAGE_SIZE 4096 ++/** Hyper-V page shift. */ ++#define GIM_HV_PAGE_SHIFT 12 ++ ++/** Microsoft Hyper-V vendor signature. */ ++#define GIM_HV_VENDOR_MICROSOFT "Microsoft Hv" ++#define GIM_HV_VENDOR_VBOX "VBoxVBoxVBox" ++ /** * MMIO2 region indices. + */ diff --git a/src/VBox/VMM/include/NEMInternal.h b/src/VBox/VMM/include/NEMInternal.h -index e0817e219c..4a71e004b0 100644 +index 18aecd8c02..3d77505b29 100644 --- a/src/VBox/VMM/include/NEMInternal.h +++ b/src/VBox/VMM/include/NEMInternal.h -@@ -35,13 +35,24 @@ +@@ -35,8 +35,17 @@ #include #include #include /* For CPUMCPUVENDOR. */ @@ -4999,14 +4853,16 @@ index e0817e219c..4a71e004b0 100644 #ifdef RT_OS_WINDOWS #include #include - #elif defined(RT_OS_DARWIN) - # include "VMXInternal.h" +@@ -46,6 +55,8 @@ + # else + # include "VMXInternal.h" + # endif +#elif defined(RT_OS_LINUX) +# include #endif RT_C_DECLS_BEGIN -@@ -207,6 +218,9 @@ typedef struct NEM +@@ -247,6 +258,9 @@ typedef struct NEM uint16_t idPrevSlot; /** Memory slot ID allocation bitmap. */ uint64_t bmSlotIds[_32K / 8 / sizeof(uint64_t)]; @@ -5016,7 +4872,7 @@ index e0817e219c..4a71e004b0 100644 #elif defined(RT_OS_WINDOWS) /** Set if we've created the EMTs. */ -@@ -354,11 +368,28 @@ typedef struct NEMCPU +@@ -417,7 +431,9 @@ typedef struct NEMCPU bool fGCMTrapXcptDE : 1; #if defined(RT_OS_LINUX) @@ -5027,7 +4883,10 @@ index e0817e219c..4a71e004b0 100644 /** The KVM VCpu file descriptor. */ int32_t fdVCpu; /** Pointer to the KVM_RUN data exchange region. */ - R3PTRTYPE(struct kvm_run *) pRun; +@@ -430,6 +446,21 @@ typedef struct NEMCPU + /** Status of the FIQ line when last seen. */ + bool fFiqLastSeen; + # else +#ifdef VBOX_WITH_KVM_NESTING + /** KVM stats file descriptor for binary statistics */ + int statsFd; @@ -5045,12 +4904,12 @@ index e0817e219c..4a71e004b0 100644 + /** The MSR_IA32_APICBASE value known to KVM. */ uint64_t uKvmApicBase; - -@@ -666,4 +697,3 @@ int nemHCNativeNotifyPhysPageAllocated(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS + #endif +@@ -760,4 +791,3 @@ int nemHCNativeNotifyPhysPageAllocated(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS RT_C_DECLS_END #endif /* !VMM_INCLUDED_SRC_include_NEMInternal_h */ - -- -2.43.0 +2.45.0 diff --git a/patches/0002-pci-passthrough-add-VFIO-implementation.patch b/patches/0002-pci-passthrough-add-VFIO-implementation.patch index 10847867..caf65826 100644 --- a/patches/0002-pci-passthrough-add-VFIO-implementation.patch +++ b/patches/0002-pci-passthrough-add-VFIO-implementation.patch @@ -18,7 +18,7 @@ license: vfio include/svp/pci.h | 444 +++++++++ src/VBox/Devices/Bus/DevVfio.cpp | 154 +++ src/VBox/Devices/Bus/DevVfio.h | 412 ++++++++ - src/VBox/Devices/Bus/VfioDevice.cpp | 910 ++++++++++++++++++ + src/VBox/Devices/Bus/VfioDevice.cpp | 912 ++++++++++++++++++ src/VBox/Devices/Makefile.kmk | 6 + src/VBox/Devices/build/VBoxDD.cpp | 3 + src/VBox/Devices/build/VBoxDD.h | 1 + @@ -28,21 +28,21 @@ license: vfio src/VBox/Main/include/ConsoleImpl.h | 1 + src/VBox/Main/include/MachineImpl.h | 5 + .../Main/src-client/BusAssignmentManager.cpp | 15 +- - src/VBox/Main/src-client/ConsoleImpl2.cpp | 62 ++ + .../src-client/ConsoleImplConfigCommon.cpp | 1 - src/VBox/Main/src-server/MachineImpl.cpp | 91 ++ - src/VBox/Main/xml/Settings.cpp | 56 ++ + src/VBox/Main/xml/Settings.cpp | 57 ++ src/VBox/Runtime/VBox/log-vbox.cpp | 1 + - 20 files changed, 2240 insertions(+), 7 deletions(-) + 20 files changed, 2181 insertions(+), 8 deletions(-) create mode 100644 vboxsrc/include/svp/pci.h create mode 100644 vboxsrc/src/VBox/Devices/Bus/DevVfio.cpp create mode 100644 vboxsrc/src/VBox/Devices/Bus/DevVfio.h create mode 100644 vboxsrc/src/VBox/Devices/Bus/VfioDevice.cpp diff --git a/include/VBox/log.h b/include/VBox/log.h -index a64390200a..a908db54c1 100644 +index 5a4193dbcc..7d434c208b 100644 --- a/include/VBox/log.h +++ b/include/VBox/log.h -@@ -178,6 +178,8 @@ typedef enum VBOXLOGGROUP +@@ -182,6 +182,8 @@ typedef enum VBOXLOGGROUP LOG_GROUP_DEV_SMC, /** Trusted Platform Module Device group. */ LOG_GROUP_DEV_TPM, @@ -51,7 +51,7 @@ index a64390200a..a908db54c1 100644 /** VGA Device group. */ LOG_GROUP_DEV_VGA, /** Virtio PCI Device group. */ -@@ -908,6 +910,7 @@ typedef enum VBOXLOGGROUP +@@ -930,6 +932,7 @@ typedef enum VBOXLOGGROUP "DEV_SERIAL", \ "DEV_SMC", \ "DEV_TPM", \ @@ -60,7 +60,7 @@ index a64390200a..a908db54c1 100644 "DEV_VIRTIO", \ "DEV_VIRTIO_NET", \ diff --git a/include/VBox/pci.h b/include/VBox/pci.h -index cd28b08375..41124406c5 100644 +index 7a51a32e94..7d6dd54e81 100644 --- a/include/VBox/pci.h +++ b/include/VBox/pci.h @@ -631,6 +631,8 @@ typedef enum PCIADDRTYPE @@ -73,10 +73,10 @@ index cd28b08375..41124406c5 100644 #define PCI_ROM_SLOT VBOX_PCI_ROM_SLOT /**< deprecated */ #define PCI_NUM_REGIONS VBOX_PCI_NUM_REGIONS /**< deprecated */ diff --git a/include/VBox/settings.h b/include/VBox/settings.h -index 674e169f9b..78bcbf6bc6 100644 +index cd6cbb9d04..e91d03c66b 100644 --- a/include/VBox/settings.h +++ b/include/VBox/settings.h -@@ -1116,6 +1116,22 @@ struct HostPCIDeviceAttachment +@@ -1126,6 +1126,22 @@ struct HostPCIDeviceAttachment typedef std::list HostPCIDeviceAttachmentList; @@ -99,7 +99,7 @@ index 674e169f9b..78bcbf6bc6 100644 /** * A device attached to a storage controller. This can either be a * hard disk or a DVD drive or a floppy drive and also specifies -@@ -1313,6 +1329,8 @@ struct Hardware +@@ -1390,6 +1406,8 @@ struct Hardware IOSettings ioSettings; // requires settings version 1.10 (VirtualBox 3.2) HostPCIDeviceAttachmentList pciAttachments; // requires settings version 1.12 (VirtualBox 4.1) @@ -1138,10 +1138,10 @@ index 0000000000..cfe384d7a1 +typedef VFIODEV *PVFIODEV; diff --git a/src/VBox/Devices/Bus/VfioDevice.cpp b/src/VBox/Devices/Bus/VfioDevice.cpp new file mode 100644 -index 0000000000..d8cf3ef1b0 +index 0000000000..6bf1945828 --- /dev/null +++ b/src/VBox/Devices/Bus/VfioDevice.cpp -@@ -0,0 +1,910 @@ +@@ -0,0 +1,912 @@ +/* + * Copyright (C) Cyberus Technology GmbH. + * @@ -1161,6 +1161,9 @@ index 0000000000..d8cf3ef1b0 + * SPDX-License-Identifier: GPL-3.0-or-later + */ + ++#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */ ++#include ++ +#include "DevVfio.h" + +#include @@ -1168,7 +1171,6 @@ index 0000000000..d8cf3ef1b0 +#include +#include +#include -+#include +#include "DevPciInternal.h" + +#include @@ -2053,19 +2055,19 @@ index 0000000000..d8cf3ef1b0 + return {barValue & std::numeric_limits::max()}; +} diff --git a/src/VBox/Devices/Makefile.kmk b/src/VBox/Devices/Makefile.kmk -index 3e22f141b8..90f01dbd20 100644 +index e31080d7af..a7c089caa2 100644 --- a/src/VBox/Devices/Makefile.kmk +++ b/src/VBox/Devices/Makefile.kmk -@@ -187,6 +187,8 @@ if !defined(VBOX_ONLY_EXTPACKS) && "$(intersects $(KBUILD_TARGET_ARCH),$(VBOX_SU +@@ -190,6 +190,8 @@ if !defined(VBOX_ONLY_EXTPACKS) && "$(intersects $(KBUILD_TARGET_ARCH),$(VBOX_SU Input/UsbMouse.cpp \ Bus/DevPCI.cpp \ Bus/DevPciIch9.cpp \ -+ Bus/DevVfio.cpp \ -+ Bus/VfioDevice.cpp \ ++ $(if-expr defined(VBOX_WITH_KVM), Bus/DevVfio.cpp,) \ ++ $(if-expr defined(VBOX_WITH_KVM), Bus/VfioDevice.cpp,) \ Bus/MsiCommon.cpp \ Bus/MsixCommon.cpp \ $(if $(VBOX_WITH_IOMMU_AMD),Bus/DevIommuAmd.cpp,) \ -@@ -252,6 +254,10 @@ if !defined(VBOX_ONLY_EXTPACKS) && "$(intersects $(KBUILD_TARGET_ARCH),$(VBOX_SU +@@ -256,6 +258,10 @@ if !defined(VBOX_ONLY_EXTPACKS) && "$(intersects $(KBUILD_TARGET_ARCH),$(VBOX_SU VBoxDD_SOURCES += Storage/DrvHostFloppy.cpp endif @@ -2077,7 +2079,7 @@ index 3e22f141b8..90f01dbd20 100644 ifn1of ($(KBUILD_TARGET), darwin) VBoxDD_SOURCES += Storage/HBDMgmt-generic.cpp diff --git a/src/VBox/Devices/build/VBoxDD.cpp b/src/VBox/Devices/build/VBoxDD.cpp -index 1e88822d41..8e51da7c4f 100644 +index 32a67a08f5..f355b992a6 100644 --- a/src/VBox/Devices/build/VBoxDD.cpp +++ b/src/VBox/Devices/build/VBoxDD.cpp @@ -218,6 +218,9 @@ extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t @@ -2091,7 +2093,7 @@ index 1e88822d41..8e51da7c4f 100644 if (RT_FAILURE(rc)) return rc; diff --git a/src/VBox/Devices/build/VBoxDD.h b/src/VBox/Devices/build/VBoxDD.h -index e451abcdd5..0927d8a327 100644 +index 557d071213..ef70e457af 100644 --- a/src/VBox/Devices/build/VBoxDD.h +++ b/src/VBox/Devices/build/VBoxDD.h @@ -107,6 +107,7 @@ extern const PDMDEVREG g_DeviceEFI; @@ -2103,10 +2105,10 @@ index e451abcdd5..0927d8a327 100644 extern const PDMDEVREG g_DeviceLPC; #ifdef VBOX_WITH_VIRTUALKD diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp -index 05bed8bf7d..d53ba59fcf 100644 +index e229f4119f..64bf2b1df7 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp -@@ -2685,6 +2685,30 @@ HRESULT showVMInfo(ComPtr pVirtualBox, +@@ -2748,6 +2748,30 @@ HRESULT showVMInfo(ComPtr pVirtualBox, /* Host PCI passthrough devices */ #endif @@ -2138,10 +2140,10 @@ index 05bed8bf7d..d53ba59fcf 100644 * Bandwidth groups */ diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp -index 4a3607250a..aa3ecf7986 100644 +index c433fc12d2..6c80d40e06 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp -@@ -234,6 +234,8 @@ enum +@@ -214,6 +214,8 @@ enum MODIFYVM_ATTACH_PCI, MODIFYVM_DETACH_PCI, #endif @@ -2150,7 +2152,7 @@ index 4a3607250a..aa3ecf7986 100644 #ifdef VBOX_WITH_USB_CARDREADER MODIFYVM_USBCARDREADER, #endif -@@ -470,6 +472,8 @@ static const RTGETOPTDEF g_aModifyVMOptions[] = +@@ -466,6 +468,8 @@ static const RTGETOPTDEF g_aModifyVMOptions[] = OPT2("--pci-attach", "--pciattach", MODIFYVM_ATTACH_PCI, RTGETOPT_REQ_STRING), OPT2("--pci-detach", "--pcidetach", MODIFYVM_DETACH_PCI, RTGETOPT_REQ_STRING), #endif @@ -2159,7 +2161,7 @@ index 4a3607250a..aa3ecf7986 100644 #ifdef VBOX_WITH_USB_CARDREADER OPT2("--usb-card-reader", "--usbcardreader", MODIFYVM_USBCARDREADER, RTGETOPT_REQ_BOOL_ONOFF), #endif -@@ -3500,6 +3504,17 @@ RTEXITCODE handleModifyVM(HandlerArg *a) +@@ -3593,6 +3597,17 @@ RTEXITCODE handleModifyVM(HandlerArg *a) break; } #endif @@ -2178,10 +2180,10 @@ index 4a3607250a..aa3ecf7986 100644 #ifdef VBOX_WITH_USB_CARDREADER case MODIFYVM_USBCARDREADER: diff --git a/src/VBox/Main/idl/VirtualBox.xidl b/src/VBox/Main/idl/VirtualBox.xidl -index 8654afec6d..26e9d94aa0 100644 +index b4472bb330..ec1e4974c7 100644 --- a/src/VBox/Main/idl/VirtualBox.xidl +++ b/src/VBox/Main/idl/VirtualBox.xidl -@@ -7336,6 +7336,12 @@ +@@ -8055,6 +8055,12 @@ @@ -2194,7 +2196,7 @@ index 8654afec6d..26e9d94aa0 100644 Bandwidth control manager. -@@ -8478,6 +8484,24 @@ +@@ -9207,6 +9213,24 @@ @@ -2220,10 +2222,10 @@ index 8654afec6d..26e9d94aa0 100644 diff --git a/src/VBox/Main/include/ConsoleImpl.h b/src/VBox/Main/include/ConsoleImpl.h -index fdea838fc0..e1fc1f9b4d 100644 +index 366f1b39ad..1d3e8155d6 100644 --- a/src/VBox/Main/include/ConsoleImpl.h +++ b/src/VBox/Main/include/ConsoleImpl.h -@@ -807,6 +807,7 @@ private: +@@ -847,6 +847,7 @@ private: bool fForce); HRESULT i_attachRawPCIDevices(PUVM pUVM, BusAssignmentManager *BusMgr, PCFGMNODE pDevices); @@ -2232,10 +2234,10 @@ index fdea838fc0..e1fc1f9b4d 100644 typedef struct LEDSET *PLEDSET; PPDMLED volatile *i_getLedSet(uint32_t iLedSet); diff --git a/src/VBox/Main/include/MachineImpl.h b/src/VBox/Main/include/MachineImpl.h -index 311a80bdc2..5a90896947 100644 +index dc11e96d59..3b66f995c5 100644 --- a/src/VBox/Main/include/MachineImpl.h +++ b/src/VBox/Main/include/MachineImpl.h -@@ -364,6 +364,8 @@ public: +@@ -339,6 +339,8 @@ public: typedef std::list > PCIDeviceAssignmentList; PCIDeviceAssignmentList mPCIDeviceAssignments; @@ -2244,7 +2246,7 @@ index 311a80bdc2..5a90896947 100644 settings::Debugging mDebugging; settings::Autostart mAutostart; -@@ -1017,6 +1019,7 @@ private: +@@ -1011,6 +1013,7 @@ private: HRESULT getIOCacheSize(ULONG *aIOCacheSize); HRESULT setIOCacheSize(ULONG aIOCacheSize); HRESULT getPCIDeviceAssignments(std::vector > &aPCIDeviceAssignments); @@ -2252,7 +2254,7 @@ index 311a80bdc2..5a90896947 100644 HRESULT getBandwidthControl(ComPtr &aBandwidthControl); HRESULT getTracingEnabled(BOOL *aTracingEnabled); HRESULT setTracingEnabled(BOOL aTracingEnabled); -@@ -1114,6 +1117,8 @@ private: +@@ -1110,6 +1113,8 @@ private: LONG aDesiredGuestAddress, BOOL aTryToUnbind); HRESULT detachHostPCIDevice(LONG aHostAddress); @@ -2262,7 +2264,7 @@ index 311a80bdc2..5a90896947 100644 ComPtr &aAdapter); HRESULT addStorageController(const com::Utf8Str &aName, diff --git a/src/VBox/Main/src-client/BusAssignmentManager.cpp b/src/VBox/Main/src-client/BusAssignmentManager.cpp -index a8d36d0717..b4665d696c 100644 +index 9f87323810..dbdffead42 100644 --- a/src/VBox/Main/src-client/BusAssignmentManager.cpp +++ b/src/VBox/Main/src-client/BusAssignmentManager.cpp @@ -109,17 +109,18 @@ static const DeviceAssignmentRule g_aGenericRules[] = @@ -2291,91 +2293,23 @@ index a8d36d0717..b4665d696c 100644 /* ISA/LPC controller */ {"lpc", 0, 31, 0, 0}, -diff --git a/src/VBox/Main/src-client/ConsoleImpl2.cpp b/src/VBox/Main/src-client/ConsoleImpl2.cpp -index cf57754ded..cc919754dc 100644 ---- a/src/VBox/Main/src-client/ConsoleImpl2.cpp -+++ b/src/VBox/Main/src-client/ConsoleImpl2.cpp -@@ -677,6 +677,66 @@ HRESULT Console::i_attachRawPCIDevices(PUVM pUVM, BusAssignmentManager *pBusMgr, - #endif - +diff --git a/src/VBox/Main/src-client/ConsoleImplConfigCommon.cpp b/src/VBox/Main/src-client/ConsoleImplConfigCommon.cpp +index f0b2ee0f0f..6ebebb28cc 100644 +--- a/src/VBox/Main/src-client/ConsoleImplConfigCommon.cpp ++++ b/src/VBox/Main/src-client/ConsoleImplConfigCommon.cpp +@@ -536,7 +536,6 @@ int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, Au + return VERR_PLATFORM_ARCH_NOT_SUPPORTED; + } -+HRESULT Console::i_attachVfioDevices(BusAssignmentManager *pBusMgr, PCFGMNODE pDevices, PCVMMR3VTABLE pVMM) -+{ -+ HRESULT hrc {S_OK}; -+ PCFGMNODE pInst{NULL}; -+ PCFGMNODE pCfg{NULL}; -+ PCFGMNODE pVfioDevs {NULL}; -+ -+ ComPtr aMachine {i_machine()}; -+ -+ SafeArray vfioDevices; -+ hrc = aMachine->COMGETTER(VFIODeviceAssignments)(ComSafeArrayAsOutParam(vfioDevices)); -+ -+ if (hrc != S_OK || vfioDevices.size() == 0) -+ { -+ return hrc; -+ } -+ -+ if (vfioDevices.size() > 0) -+ { -+ InsertConfigNode(pDevices, "VfioDev", &pVfioDevs); -+ -+ /* -+ * As an IOMMU is needed for VFIO devices we need to force RAM preallocation -+ */ -+ -+ //PCFGMNODE pRoot = pVMM->pfnCFGMR3GetParent(pDevices); Assert(pRoot); -+ //pVMM->pfnCFGMR3RemoveValue(pRoot, "RamPreAlloc"); -+ //InsertConfigInteger(pRoot, "RamPreAlloc", 1); -+ } -+ -+ for (size_t iDev{0}; iDev < vfioDevices.size(); ++iDev) -+ { -+ Utf8Str devicePath {vfioDevices[iDev]}; -+ -+ InsertConfigNode(pVfioDevs, Utf8StrFmt("%d", iDev).c_str(), &pInst); -+ InsertConfigInteger(pInst, "Trusted", 1); -+ InsertConfigNode(pInst, "Config", &pCfg); -+ InsertConfigString(pCfg, "sysfsPath", devicePath); -+ -+ PCIBusAddress guestAddress; -+ bool fGuestAddressRequired{false}; -+ /* -+ * The fGuestAddressRequired flag of the assignPCIDevice call can ask for a certain BDF in the guest. -+ * If a guestAddress is required for a certain device guestAddress has to be set to the certain BDF. -+ * The call will fail, if the address is already in use. -+ */ -+ hrc = pBusMgr->assignPCIDevice("vfio", pInst, guestAddress, fGuestAddressRequired); -+ if (hrc != S_OK) -+ { -+ return hrc; -+ } -+ -+ InsertConfigInteger(pCfg, "GuestPCIBusNo", guestAddress.miBus); -+ InsertConfigInteger(pCfg, "GuestPCIDeviceNo", guestAddress.miDevice); -+ InsertConfigInteger(pCfg, "GuestPCIFunctionNo", guestAddress.miFn); -+ } -+ -+ return hrc; -+} -+ +- /** - * Updates the device type for a LED. + * Configures an audio driver via CFGM by getting (optional) values from extra data. * -@@ -1737,6 +1797,8 @@ int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, Au - } - } - -+ hrc = i_attachVfioDevices(pBusMgr, pDevices, pVMM); H(); -+ - /* - * Enable the following devices: HPET, SMC and LPC on MacOS X guests or on ICH9 chipset - */ diff --git a/src/VBox/Main/src-server/MachineImpl.cpp b/src/VBox/Main/src-server/MachineImpl.cpp -index c3c63d5989..e3d3287a7d 100644 +index 59fc285582..0fb5e25735 100644 --- a/src/VBox/Main/src-server/MachineImpl.cpp +++ b/src/VBox/Main/src-server/MachineImpl.cpp -@@ -7064,6 +7064,80 @@ HRESULT Machine::getPCIDeviceAssignments(std::vector &aBandwidthControl) { mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam()); -@@ -9474,6 +9548,12 @@ HRESULT Machine::i_loadHardware(const Guid *puuidRegistry, +@@ -8853,6 +8927,12 @@ HRESULT Machine::i_loadHardware(const Guid *puuidRegistry, mHWData->mPCIDeviceAssignments.push_back(pda); } @@ -2469,7 +2403,7 @@ index c3c63d5989..e3d3287a7d 100644 /* * (The following isn't really real hardware, but it lives in HWData * for reasons of convenience.) -@@ -10887,6 +10967,17 @@ HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *p +@@ -10234,6 +10314,17 @@ HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *p data.pciAttachments.push_back(hpda); } @@ -2488,10 +2422,10 @@ index c3c63d5989..e3d3287a7d 100644 data.llGuestProperties.clear(); #ifdef VBOX_WITH_GUEST_PROPS diff --git a/src/VBox/Main/xml/Settings.cpp b/src/VBox/Main/xml/Settings.cpp -index fa1c1d82a8..e818eeb23f 100644 +index ea46a45ab8..a75cde069c 100644 --- a/src/VBox/Main/xml/Settings.cpp +++ b/src/VBox/Main/xml/Settings.cpp -@@ -3902,6 +3902,21 @@ bool HostPCIDeviceAttachment::operator==(const HostPCIDeviceAttachment &a) const +@@ -3967,6 +3967,22 @@ bool HostPCIDeviceAttachment::operator==(const HostPCIDeviceAttachment &a) const && strDeviceName == a.strDeviceName); } @@ -2510,10 +2444,11 @@ index fa1c1d82a8..e818eeb23f 100644 + return (this == &a) + || (strDevicePath == a.strDevicePath); +} - - /** - * Constructor. Needs to set sane defaults which stand the test of time. -@@ -4089,6 +4104,7 @@ bool Hardware::operator==(const Hardware& h) const ++ + #ifdef VBOX_WITH_VIRT_ARMV8 + PlatformARM::PlatformARM() + { +@@ -4214,6 +4230,7 @@ bool Hardware::operator==(const Hardware& h) const && llGuestProperties == h.llGuestProperties && ioSettings == h.ioSettings && pciAttachments == h.pciAttachments @@ -2521,7 +2456,7 @@ index fa1c1d82a8..e818eeb23f 100644 && strDefaultFrontend == h.strDefaultFrontend); } -@@ -5842,6 +5858,26 @@ void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware, +@@ -6125,6 +6142,26 @@ void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware, } } } @@ -2548,7 +2483,7 @@ index fa1c1d82a8..e818eeb23f 100644 else if (pelmHwChild->nameEquals("EmulatedUSB")) { const xml::ElementNode *pelmCardReader; -@@ -7927,6 +7963,20 @@ void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent, +@@ -8438,6 +8475,20 @@ void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent, } } @@ -2569,7 +2504,7 @@ index fa1c1d82a8..e818eeb23f 100644 if ( m->sv >= SettingsVersion_v1_12 && hw.fEmulatedUSBCardReader) { -@@ -9143,6 +9193,12 @@ void MachineConfigFile::bumpSettingsVersionIfNeeded() +@@ -9729,6 +9780,12 @@ void MachineConfigFile::bumpSettingsVersionIfNeeded() return; } } @@ -2583,7 +2518,7 @@ index fa1c1d82a8..e818eeb23f 100644 if (m->sv < SettingsVersion_v1_16) diff --git a/src/VBox/Runtime/VBox/log-vbox.cpp b/src/VBox/Runtime/VBox/log-vbox.cpp -index 2e82f859f7..9d9c5d731e 100644 +index c1edf5a81b..522fd4c0cd 100644 --- a/src/VBox/Runtime/VBox/log-vbox.cpp +++ b/src/VBox/Runtime/VBox/log-vbox.cpp @@ -272,6 +272,7 @@ RTDECL(PRTLOGGER) RTLogDefaultInit(void) @@ -2595,5 +2530,5 @@ index 2e82f859f7..9d9c5d731e 100644 ASSERT_LOG_GROUP(DEV_VIRTIO); ASSERT_LOG_GROUP(DEV_VIRTIO_NET); -- -2.43.0 +2.45.0 diff --git a/patches/0003-virtio-gpu-Add-virtiogpu-implementation.patch b/patches/0003-virtio-gpu-Add-virtiogpu-implementation.patch deleted file mode 100644 index 9f8180a9..00000000 --- a/patches/0003-virtio-gpu-Add-virtiogpu-implementation.patch +++ /dev/null @@ -1,5287 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Sebastian Eydam -Date: Mon, 15 Aug 2022 14:13:14 +0200 -Subject: [PATCH] virtio-gpu: Add virtiogpu implementation - -virtiogpu: enable hotplug support - -Some hardcoded display IDs were removed and if we change the display -layout via "VBoxManage modifyvm setscreenlayout" we pass the -required information to enable/disable monitors to the virtio gpu. - -Further, the VBoxManage setscreenlayout call is extended to pass the -display information to the virtiogpu in case it is used as a graphics -adapter. - -virtio-gpu: Disable x2apic msr hint conditionally - -If the VirtioGPU is used as a graphics controller and the guest is -configured with >8 CPUs, we disable the X2APIC_MSR HyperV hint. - -We do this because there are known issues with the DVServerKMD virtio -gpu driver in Windows 10, that prevent the Windows from booting if the -specific HyperV hint is set. Not setting the hint allows the Windows to -boot. Therefore, we only disable the hint in the specific combination of -VirtioGPU + >8 CPUs in order to not affect guest performance in all -other cases. - -VBox: virtio: add PDMR3QueryDevice to VMM vTable again - -VBox:Virtio: Move from SVP include directory to cyberus include directory - -VBox:Virtio: rename SVP Display to CBS Display - -virtio: remove slvm dependency - -vga: re-add dummy connector attachment - -We previously had some VBOX_WITH_SVP define around the code connecting -our dummy connector when switching from the VGA display to some custom -display (either GVT or VirtipGPU). - -Having this define led to subtle bugs when using SR-IOV with a non SVP -backend like KVM. It led to crashes in the SVGA device when e.g. -shutting down a VM currently running on the VirtioGPU. - -As this define prevented SR-IOV to work with KVM, and also prevented -SR-IOV with vanilla VBox, we remove it. - -virtiocore: workaround lost event - -The Intel DV driver does not recognize the display changed event we are -issuing. We noticed, that when setting both queue interrupt + display -event in the ISR solves this problem. It is unclear why this is the case -at the moment. - -license: virtiogpu ---- - include/VBox/log.h | 3 + - include/VBox/vmm/pdmcommon.h | 5 + - include/VBox/vmm/pdmifs.h | 11 + - include/VBox/vmm/vmmr3vtable-def.h | 1 + - include/cyberus/edid.hpp | 423 +++++++++++ - include/{svp => cyberus}/pci.h | 0 - src/VBox/Devices/Bus/DevVfio.h | 2 +- - src/VBox/Devices/Graphics/DevVGA.cpp | 48 ++ - src/VBox/Devices/Graphics/DevVirtioGpu.cpp | 407 +++++++++++ - src/VBox/Devices/Graphics/DevVirtioGpu.hpp | 373 ++++++++++ - .../Graphics/DevVirtioGpuCmdHandler.cpp | 687 ++++++++++++++++++ - .../Graphics/DevVirtioGpuCmdHandler.hpp | 353 +++++++++ - .../Graphics/DevVirtioGpuDefinitions.hpp | 360 +++++++++ - .../Graphics/DevVirtioGpuDisplayManager.cpp | 413 +++++++++++ - .../Graphics/DevVirtioGpuDisplayManager.hpp | 213 ++++++ - .../Devices/Graphics/DevVirtioGpuResource.hpp | 80 ++ - .../Graphics/DevVirtioGpuVBoxStubs.cpp | 342 +++++++++ - .../Graphics/DevVirtioGpuVBoxStubs.hpp | 71 ++ - src/VBox/Devices/Makefile.kmk | 10 + - src/VBox/Devices/VirtIO/VirtioCore.cpp | 2 +- - src/VBox/Devices/build/VBoxDD.cpp | 3 + - src/VBox/Devices/build/VBoxDD.h | 1 + - src/VBox/Devices/testcase/Makefile.kmk | 37 +- - .../Devices/testcase/tstVirtioGpuAdapter.hpp | 181 +++++ - .../testcase/tstVirtioGpuCmdHandling.cpp | 450 ++++++++++++ - .../Frontends/VBoxManage/VBoxManageInfo.cpp | 12 + - .../VBoxManage/VBoxManageModifyVM.cpp | 6 + - .../src/converter/UIConverterBackendCOM.cpp | 4 + - src/VBox/Main/idl/VirtualBox.xidl | 6 + - src/VBox/Main/include/ConsoleImpl.h | 7 +- - .../Main/src-client/BusAssignmentManager.cpp | 4 +- - src/VBox/Main/src-client/ConsoleImpl2.cpp | 98 ++- - src/VBox/Main/src-client/DisplayImpl.cpp | 41 ++ - .../Main/src-server/GraphicsAdapterImpl.cpp | 2 + - .../Main/src-server/SystemPropertiesImpl.cpp | 2 + - src/VBox/Main/xml/Settings.cpp | 6 + - src/VBox/Main/xml/VirtualBox-settings.xsd | 2 + - src/VBox/Runtime/VBox/log-vbox.cpp | 1 + - src/VBox/VMM/VMMR3/GIMHv.cpp | 14 +- - 39 files changed, 4666 insertions(+), 15 deletions(-) - create mode 100644 vboxsrc/include/cyberus/edid.hpp - rename vboxsrc/include/{svp => cyberus}/pci.h (100%) - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpu.cpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpu.hpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpuCmdHandler.cpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpuCmdHandler.hpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpuDefinitions.hpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpuDisplayManager.cpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpuDisplayManager.hpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpuResource.hpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpuVBoxStubs.cpp - create mode 100644 vboxsrc/src/VBox/Devices/Graphics/DevVirtioGpuVBoxStubs.hpp - create mode 100644 vboxsrc/src/VBox/Devices/testcase/tstVirtioGpuAdapter.hpp - create mode 100644 vboxsrc/src/VBox/Devices/testcase/tstVirtioGpuCmdHandling.cpp - -diff --git a/include/VBox/log.h b/include/VBox/log.h -index a908db54c1..d59b330aea 100644 ---- a/include/VBox/log.h -+++ b/include/VBox/log.h -@@ -184,6 +184,8 @@ typedef enum VBOXLOGGROUP - LOG_GROUP_DEV_VGA, - /** Virtio PCI Device group. */ - LOG_GROUP_DEV_VIRTIO, -+ /** Virtio GPU Device group. */ -+ LOG_GROUP_DEV_VIRTIO_GPU, - /** Virtio Network Device group. */ - LOG_GROUP_DEV_VIRTIO_NET, - /** VMM Device group. */ -@@ -913,6 +915,7 @@ typedef enum VBOXLOGGROUP - "DEV_VFIO", \ - "DEV_VGA", \ - "DEV_VIRTIO", \ -+ "DEV_VIRTIO_GPU", \ - "DEV_VIRTIO_NET", \ - "DEV_VMM", \ - "DEV_VMM_BACKDOOR", \ -diff --git a/include/VBox/vmm/pdmcommon.h b/include/VBox/vmm/pdmcommon.h -index cbad79983e..8746e53afc 100644 ---- a/include/VBox/vmm/pdmcommon.h -+++ b/include/VBox/vmm/pdmcommon.h -@@ -111,6 +111,11 @@ - #define PDM_TACH_FLAGS_NO_CALLBACKS RT_BIT_32(1) - /** @} */ - -+/** This flag is used in the pfnAttach call for the vga device and indicates -+ * that only a dummy driver should be attached. This is used to "disable" the -+ * vga device. -+ */ -+#define PDM_ATTACH_DUMMY_DRIVER RT_BIT_32(31) - - /** - * Is asynchronous handling of suspend or power off notification completed? -diff --git a/include/VBox/vmm/pdmifs.h b/include/VBox/vmm/pdmifs.h -index c4eaeb1fca..86a3a94afb 100644 ---- a/include/VBox/vmm/pdmifs.h -+++ b/include/VBox/vmm/pdmifs.h -@@ -42,6 +42,9 @@ - #include - #include - -+// For VMMDevDisplayDef -+#include -+ - - RT_C_DECLS_BEGIN - -@@ -552,6 +555,14 @@ typedef struct PDMIKEYBOARDCONNECTOR - #define PDMIKEYBOARDCONNECTOR_IID "db3f7bd5-953e-436f-9f8e-077905a92d82" - - -+typedef struct PDMIVIRTIOGPUPORT -+{ -+ DECLR3CALLBACKMEMBER(void, pfnDisplayChanged, (PPDMDEVINS pDevIns, uint32_t numDisplays, VMMDevDisplayDef* displayDefs)); -+} PDMIVIRTIOGPUPORT; -+ -+typedef struct PDMIVIRTIOGPUPORT *PPDMIVIRTIOGPUPORT; -+ -+# define PDMIVIRTIOGPUPORT_IID "db3f7bd5-baba-436f-9f8e-077905a92d82" - - /** Pointer to a display port interface. */ - typedef struct PDMIDISPLAYPORT *PPDMIDISPLAYPORT; -diff --git a/include/VBox/vmm/vmmr3vtable-def.h b/include/VBox/vmm/vmmr3vtable-def.h -index e603a58997..b4207844f5 100644 ---- a/include/VBox/vmm/vmmr3vtable-def.h -+++ b/include/VBox/vmm/vmmr3vtable-def.h -@@ -595,6 +595,7 @@ VTABLE_ENTRY(PDMR3DeviceDetach) - VTABLE_ENTRY(PDMR3DriverAttach) - VTABLE_ENTRY(PDMR3DriverDetach) - VTABLE_ENTRY(PDMR3NsBwGroupSetLimit) -+VTABLE_ENTRY(PDMR3QueryDevice) - VTABLE_ENTRY(PDMR3QueryDeviceLun) - VTABLE_ENTRY(PDMR3QueryDriverOnLun) - VTABLE_ENTRY(PDMR3QueryLun) -diff --git a/include/cyberus/edid.hpp b/include/cyberus/edid.hpp -new file mode 100644 -index 0000000000..b05b59ebb3 ---- /dev/null -+++ b/include/cyberus/edid.hpp -@@ -0,0 +1,423 @@ -+#pragma once -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * The Extended Display Identification Data structure Version 1.4 Structure Definitions -+ * The EDID structures are implemented based on the VESA-EEDID-A2 Specification -+ * from https://glenwing.github.io/docs/VESA-EEDID-A2.pdf -+ */ -+ -+/* -+ * The EDID Standard Timings Definition (Section 3.9 VESA EEDID A2 Specification) -+ * For a list of Standard Codes please refer to the VESA DMT 1.13 Specification. -+ * Link: https://glenwing.github.io/docs/VESA-DMT-1.13.pdf -+ * The horizontal addressable pixels are calculated by the following formula: -+ * horizontalPixels = (pixelcount / 8) - 31; -+ */ -+struct __attribute__((__packed__)) EdidStandardTiming -+{ -+ uint8_t horizontalPixels; -+ -+ /* -+ * The aspectRatio is stored in bits 6 and 7 -+ */ -+ enum __attribute__((__packed__)) AspectRatio : uint8_t -+ { -+ AR_16_10 = 0x0, -+ AR_4_3 = 0x1 << 6, -+ AR_5_4 = 0x2 << 6, -+ AR_16_9 = 0x3 << 6 -+ }; -+ -+ /* -+ * The refresh rate is stored in the lower 5 bits and stored using this -+ * formula: refreshRate = + 60 HZ -+ * Example 85 HZ: 0x19 + 60 = 85 hz; refresh rate = 0x19; -+ */ -+ uint8_t aspectRatioAndRefreshRate; -+ -+ EdidStandardTiming() = default; -+ -+ EdidStandardTiming(uint32_t horizontalPixels_, AspectRatio ratio, uint8_t refreshRate) -+ { -+ assert(refreshRate >= 60); -+ horizontalPixels = (horizontalPixels_ / 8) - 31; -+ aspectRatioAndRefreshRate = (ratio & 0xc0) | ((refreshRate - 60) & 0x3f); -+ } -+}; -+ -+/* -+ * The Detailed Timing Descriptor (Section 3.10.2) -+ * -+ * The default values are extracted from a running GVT with its default EDID -+ */ -+struct __attribute__((__packed__)) EdidDetailedTimingDescriptor -+{ -+ uint16_t pixelClock {0}; -+ uint8_t hVideoLow {0x80}; -+ uint8_t hBlankingLow {0xa0}; -+ uint8_t hVideoBlankingHigh {0x70}; -+ uint8_t vVideoLow {0xb0}; -+ uint8_t vVBlankingLow {0x23}; -+ uint8_t vVideoBlankingHigh {0x40}; -+ uint8_t hFrontPorchLow {0x30}; -+ uint8_t hSyncPulseWidthLow {0x20}; -+ uint8_t vFrontPorchSyncPulseWidthlow {0x36}; -+ uint8_t vhFrontPorchSyncPulseHigh {0x00}; -+ uint8_t hVideoImageSizeLow {0x06}; -+ uint8_t vVideoImageSizeLow {0x44}; -+ uint8_t vhVideoImageSizeHigh {0x21}; -+ uint8_t horizontalBorder {0x00}; // (Section 3.12) -+ uint8_t verticalBorder {0x00}; // (Section 3.12) -+ uint8_t signalDefinitions {0x1a}; // (Table 3.22) -+}; -+ -+static_assert(sizeof(EdidDetailedTimingDescriptor) == 18, -+ "The size of the EdidDetailedTimingDescriptor must be 18 bytes!"); -+ -+/* -+ * The Display Descriptor Definitions (Section 3.10.3) -+ */ -+struct __attribute__((__packed__)) EdidDisplayDescriptorDefinitions -+{ -+ uint16_t reserved {0}; -+ uint8_t reserved1 {0}; -+ -+ enum __attribute__((__packed__)) : uint8_t -+ { -+ DisplayProductName = 0xFC, -+ DisplayRangeLimits = 0xFD, -+ DisplaySerialNumber = 0xFF, -+ } tag {DisplayProductName}; -+ -+ union __attribute__((__packed__)) -+ { -+ uint8_t reserved2 {0}; // for Serial Number and Product Name -+ uint8_t rangeLimitOffset; // for Display Range Limits (Table 3.26) -+ }; -+ -+ union __attribute__((__packed__)) -+ { -+ char productName[13] {"CBS Display"}; -+ char serialNumber[13]; -+ struct __attribute__((__packed__)) -+ { -+ uint8_t minimumVerticalRate; -+ uint8_t maximumVerticalRate; -+ uint8_t minimumHorizontalRate; -+ uint8_t maximumHorizontalRate; -+ uint8_t maximumPixelClock; -+ uint8_t videoTimingSupportFlags; -+ uint8_t videoTimingDataOrLineFeed; // (Table 3.27 and 3.28 -+ uint8_t videoTimingDataOrSpace[6]; -+ } rangeLimitsTimingDescriptor; -+ }; -+}; -+ -+static_assert(sizeof(EdidDisplayDescriptorDefinitions) == 18, -+ "The size of the EdidDisplayDescriptorDefinitions must be 18 bytes!"); -+ -+/* -+ * The EDID Base Block. -+ * All definitions in this structure are based on the VESA-EEDID-A2 Specification. -+ * The structure is implemented Based on Table 3.1 in Section 3.i EDID Format Overview -+ */ -+struct __attribute__((__packed__)) EdidBaseBlock -+{ -+ uint64_t header {0x00ffffffffffff00}; // (Section 3.3) -+ -+ /* -+ * Vendor and Product ID (Section 3.4) -+ */ -+ uint16_t manufacturerName {0x530c}; // "CBS" (Section 3.4.1) -+ uint16_t productCode {0x1}; // (Section 3.4.2) -+ uint32_t serialNumber {0x1337}; // (Section 3.4.3) -+ uint16_t manufacturingDates {0x262d}; // (Section 3.4.4) ->(WW45 2022) -+ // -+ /* -+ * EDID Version and Revision (Section 3.5) -+ */ -+ uint8_t version {0x1}; -+ uint8_t revision {0x4}; -+ -+ /* -+ * Basic Display Parameters and Features (Section 3.6) -+ */ -+ uint8_t videoInputDefinition {0xa5}; // (Section 3.6.1 and Table 3.11) -+ -+ /* -+ * The Aspect ratio or screen size (Section 3.6.2 and Table 3.12) As we can't -+ * determine the aspect ratio nor the horizontal and vertical screen size -+ * We set the value to 0 which is per Specification used for variable or -+ * unknown ARs and Screen sizes. -+ */ -+ uint16_t aspectRatio {0x0000}; -+ uint8_t displayTransferCharacteristic {0x78}; // (Section 3.6.3) -+ uint8_t supportedFeatures {0x23}; // (Section 3.6.4 and Table 3.14) -+ -+ /* -+ * Color Characteristics (Section 3.7) -+ * Note: these values are copied and modified from -+ * src/VBox/Additions/linux/drm/vbox_mode.c:vbox_set_edid -+ */ -+ uint8_t redGreenLowOrder {0xfc}; -+ uint8_t blueWhiteLowOrder {0x81}; -+ uint8_t redXHighOrder {0xa4}; -+ uint8_t redYHighOrder {0x55}; -+ uint8_t greenXHighOrder {0x4d}; -+ uint8_t greenYHighOrder {0x9d}; -+ uint8_t blueXHighOrder {0x25}; -+ uint8_t blueYHighOrder {0x12}; -+ uint8_t whiteXHighOrder {0x50}; -+ uint8_t whiteYHighOrder {0x54}; -+ -+ /* -+ * Established Timings (Section 3.8 and Table 3.18) -+ */ -+ uint8_t establishedTimings1 {0x21}; // 640x480,60HZ; 800x600,60HZ -+ uint8_t establishedTimings2 {0x8}; // 1024x768,60HZ -+ uint8_t manufacturersTimings {0x0}; -+ -+ /* -+ * Standard Timings (Section 3.9) -+ */ -+ EdidStandardTiming standardTimings[8]; -+ -+ /* -+ * The 18 Byte Descriptors (Section 3.10) -+ */ -+ EdidDetailedTimingDescriptor preferredTimingMode {}; -+ -+ /* -+ * The Second to 4th 18 byte descriptor. -+ * At the Moment we use Display Descriptor Definitions only, but -+ * DetailedTimingDescriptors are possible here as well. -+ */ -+ EdidDisplayDescriptorDefinitions displayDescriptors[3]; -+ -+ uint8_t extensionBlockCount {0}; -+ -+ uint8_t checksum; -+}; -+ -+constexpr uint32_t EDID_LENGTH {sizeof(EdidBaseBlock)}; -+ -+/* -+ * The EIA/CEA 861F Extended EDID Structures. -+ * -+ * The EDID contains the EDID Base Block and an additional EIA/CEA 861F -+ * Compliant EDID Block. -+ * -+ * The EIA/CEA 861G Block Layouts are specified in the CEA-861-F Specification (Section 7.5). -+ * Link: -+ * https://web.archive.org/web/20171201033424/https://standards.cta.tech/kwspub/published_docs/CTA-861-G_FINAL_revised_2017.pdf -+ */ -+ -+/* -+ * The CEA Data Block Header Type Byte structure. (Table 54 CEA 861-G) -+ */ -+struct __attribute__((__packed__)) CEADataBlockHeader -+{ -+ /* -+ * The CEA Data Block Codes (Table 55 EIA/CEA 861-G Specification) -+ */ -+ enum __attribute__((__packed__)) DataBlockTagCode : uint8_t -+ { -+ Audio = 1 << 5, -+ Video = 2 << 5, -+ VendorSpecific = 3 << 5, -+ SpeakerAllocation = 4 << 5, -+ VESADisplayTransferCharacteristic = 5 << 5, -+ UseExtendedTag = 7 << 5, -+ }; -+ -+ enum __attribute__((__packed__)) : uint8_t -+ { -+ LENGTH_MASK = 0x1F, -+ TAG_MASK = 0xE0, -+ }; -+ -+ uint8_t tagAndLength; -+}; -+ -+/* -+ * The Video Data Block of the EIA/CEA 861-G Specification (Section 7.5.1) -+ */ -+struct __attribute__((__packed__)) CEAVideoDataBlock : public CEADataBlockHeader -+{ -+ static constexpr uint8_t MAX_SHORT_VIDEO_DESCRIPTORS {0x1F}; -+ std::array shortVideoDescriptors; -+}; -+ -+/* -+ * The CEA Timing Extension. -+ * -+ * The Timing Extension can contain the following CEA Data Blocks: -+ * - Video Data Block -+ * - Audio Data Block -+ * - Speaker Allocation Data Block -+ * - Vendor Specific Data Block -+ * -+ * Our current use case requires Video Data Blocks only. -+ * For simplicity the Structure contains Video Data Blocks only. -+ * -+ * -+ * The Basic Layout can be seen in Table 52 and Table 53 of the EIA/CEA-861-G Specification -+ */ -+struct __attribute__((__packed__)) CEAExtendedEdid : public EdidBaseBlock -+{ -+ uint8_t eiaCeaTag {0x2}; -+ uint8_t eiaCeaRevision {0x3}; -+ uint8_t eiaCeaDetailedTimingDescriptorOffset {0x0}; -+ uint8_t eiaCeaNativeFormatsandFeatures {0x0}; -+ CEAVideoDataBlock videoDataBlock; -+ -+ /* padding or other data blocks or DTD's */ -+ std::array padding; -+ uint8_t eiaCeaChecksum {0x0}; -+}; -+ -+static_assert(sizeof(CEAExtendedEdid) == 256, "The CEAExtendedEdid is not 256 bytes large."); -+ -+/** -+ * Generates a EDID (Extended Display Identification Data) where the Preferred -+ * Timing Mode has the given resolution. -+ * -+ * The mechanism works following way: -+ * - unplug the virtual display from the vGPU -+ * - set the new EDID generated from the given resolution -+ * - plug in the virtual display -+ * -+ * For the guest OS it looks like a new monitor is connected to the graphics -+ * controller. -+ * -+ * The EDID is implemented based on the VESA-EEDID-A2 Specification. -+ * https://glenwing.github.io/docs/VESA-EEDID-A2.pdf -+ * -+ * \param xRes horizontal resolution of the virtual display -+ * \param yRes vertical resolution of the virtual display -+ * \return A Edid where the Preferred Timing Mode has the given resolution -+ */ -+template -+static inline EDID prepareEdid(uint32_t xRes, uint32_t yRes, uint32_t extensionBlockCount = 0) -+{ -+ EDID edid; -+ -+ edid.standardTimings[0] = {1920, EdidStandardTiming::AR_16_10, 60}; // 1920x1200, 60hz -+ edid.standardTimings[1] = {1920, EdidStandardTiming::AR_16_9, 60}; // 1920x1080, 60hz -+ edid.standardTimings[2] = {1680, EdidStandardTiming::AR_16_10, 60}; // 1680x1050, 60hz -+ edid.standardTimings[3] = {1600, EdidStandardTiming::AR_16_9, 60}; // 1600x900, 60hz -+ edid.standardTimings[4] = {1600, EdidStandardTiming::AR_4_3, 60}; // 1600x1200, 60hz -+ edid.standardTimings[5] = {1024, EdidStandardTiming::AR_4_3, 60}; // 1024x768, 60hz -+ edid.standardTimings[6] = {800, EdidStandardTiming::AR_4_3, 60}; // 800x600, 60hz -+ edid.standardTimings[7] = {640, EdidStandardTiming::AR_4_3, 60}; // 640x480, 60hz -+ -+ const uint32_t hblank = -+ ((edid.preferredTimingMode.hVideoBlankingHigh & 0x0f) << 8) | edid.preferredTimingMode.hBlankingLow; -+ const uint32_t vblank = -+ ((edid.preferredTimingMode.vVideoBlankingHigh & 0x0f) << 8) | edid.preferredTimingMode.vVBlankingLow; -+ const uint8_t refresh_rate = 60; -+ uint16_t clock = (xRes + hblank) * (yRes + vblank) * refresh_rate / 10000; -+ -+ edid.preferredTimingMode.pixelClock = clock; -+ -+ edid.preferredTimingMode.hVideoLow = xRes & 0xff; -+ edid.preferredTimingMode.hVideoBlankingHigh &= 0xf; -+ edid.preferredTimingMode.hVideoBlankingHigh |= (xRes >> 4) & 0xf0; -+ -+ edid.preferredTimingMode.vVideoLow = yRes & 0xff; -+ edid.preferredTimingMode.vVideoBlankingHigh &= 0xf; -+ edid.preferredTimingMode.vVideoBlankingHigh |= (yRes >> 4) & 0xf0; -+ -+ edid.displayDescriptors[0].tag = EdidDisplayDescriptorDefinitions::DisplayRangeLimits; -+ edid.displayDescriptors[0].rangeLimitOffset = 0x0; -+ edid.displayDescriptors[0].rangeLimitsTimingDescriptor.minimumVerticalRate = 0x18; -+ edid.displayDescriptors[0].rangeLimitsTimingDescriptor.maximumVerticalRate = 0x3c; -+ edid.displayDescriptors[0].rangeLimitsTimingDescriptor.minimumHorizontalRate = 0x18; -+ edid.displayDescriptors[0].rangeLimitsTimingDescriptor.maximumHorizontalRate = 0x50; -+ edid.displayDescriptors[0].rangeLimitsTimingDescriptor.maximumPixelClock = 0x11; -+ edid.displayDescriptors[0].rangeLimitsTimingDescriptor.videoTimingSupportFlags = 0x0; -+ edid.displayDescriptors[0].rangeLimitsTimingDescriptor.videoTimingDataOrLineFeed = 0x0a; -+ std::memset(&edid.displayDescriptors[0].rangeLimitsTimingDescriptor.videoTimingDataOrSpace, 0x20, -+ sizeof(edid.displayDescriptors[0].rangeLimitsTimingDescriptor.videoTimingDataOrSpace)); -+ -+ edid.displayDescriptors[1].tag = EdidDisplayDescriptorDefinitions::DisplayProductName; -+ -+ edid.displayDescriptors[2].tag = EdidDisplayDescriptorDefinitions::DisplaySerialNumber; -+ /* The EDID requires a different serial number on change, thus we add the x Resolution to the serial number. */ -+ std::string serialNumber = "Cyberus " + std::to_string(xRes); -+ std::strncpy(&edid.displayDescriptors[2].serialNumber[0], serialNumber.c_str(), -+ std::min(serialNumber.length(), sizeof(edid.displayDescriptors[2].serialNumber))); -+ -+ edid.extensionBlockCount = extensionBlockCount; -+ -+ const uint8_t edidChecksumIndex {offsetof(EdidBaseBlock, checksum)}; -+ auto edidPtr {reinterpret_cast(&edid)}; -+ auto sum {std::accumulate(edidPtr, edidPtr + edidChecksumIndex, 0)}; -+ edid.checksum = (0x100 - (sum & 0xff)) & 0xff; -+ -+ return edid; -+} -+ -+static inline std::array generateEdid(uint32_t xRes, uint32_t yRes) -+{ -+ auto edid {prepareEdid(xRes, yRes)}; -+ -+ std::array array; -+ std::memcpy(array.data(), &edid, array.size()); -+ -+ return array; -+} -+ -+static inline CEAExtendedEdid generateExtendedEdid(uint32_t xRes, uint32_t yRes) -+{ -+ CEAExtendedEdid edid {prepareEdid(xRes, yRes, 1)}; -+ -+ uint8_t timingCount {0}; -+ -+ /* -+ * All timings that can be used in Short Video Descriptors are defined -+ * in (Table 3: Video Formats CEA-861-G Specifications) indexed by their Video ID Code (VIC) -+ */ -+ auto add_timing = [&timingCount, &edid](const uint8_t vic, const bool native = false) { -+ /* -+ * The Video Data Block is able to support up to 0x1 -+ */ -+ if (timingCount <= CEAVideoDataBlock::MAX_SHORT_VIDEO_DESCRIPTORS) { -+ /* -+ * For timings with VIC < 65 a native indicator can be set (Refer Section 7.2.3 CEA 861-F Spec.) -+ * This requires a special handling. -+ */ -+ if (vic < 65 and native) { -+ edid.videoDataBlock.shortVideoDescriptors[timingCount++] = (1 << 7) | (vic & 0x7f); -+ } else { -+ edid.videoDataBlock.shortVideoDescriptors[timingCount++] = vic; -+ } -+ } -+ }; -+ -+ /* -+ * The VIC numbers taken from (Table 3: Video Formats CEA 861-G Specification) -+ * -+ * A Native resolution basically means the displays standard resolution -+ */ -+ add_timing(5, true); // 1920x1080, 60hz -+ add_timing(90); // 2560x1080, 60hz -+ add_timing(97); // 3840x2160, 60hz -+ -+ edid.videoDataBlock.tagAndLength = -+ CEADataBlockHeader::DataBlockTagCode::Video | (timingCount & CEADataBlockHeader::LENGTH_MASK); -+ edid.eiaCeaDetailedTimingDescriptorOffset = 4 + sizeof(CEAVideoDataBlock); -+ const uint8_t checksumIndex {sizeof(CEAExtendedEdid) - sizeof(EdidBaseBlock) - 1}; -+ auto edidPtr {reinterpret_cast(&edid) + sizeof(EdidBaseBlock)}; -+ auto sum {std::accumulate(edidPtr, edidPtr + checksumIndex, 0)}; -+ edid.eiaCeaChecksum = (0x100 - (sum & 0xff)) & 0xff; -+ -+ return edid; -+} -diff --git a/include/svp/pci.h b/include/cyberus/pci.h -similarity index 100% -rename from include/svp/pci.h -rename to include/cyberus/pci.h -diff --git a/src/VBox/Devices/Bus/DevVfio.h b/src/VBox/Devices/Bus/DevVfio.h -index cfe384d7a1..d6bfb0a9f2 100644 ---- a/src/VBox/Devices/Bus/DevVfio.h -+++ b/src/VBox/Devices/Bus/DevVfio.h -@@ -19,7 +19,7 @@ - - #pragma once - --#include -+#include - - #include - #include -diff --git a/src/VBox/Devices/Graphics/DevVGA.cpp b/src/VBox/Devices/Graphics/DevVGA.cpp -index e1060783bc..0e027884be 100644 ---- a/src/VBox/Devices/Graphics/DevVGA.cpp -+++ b/src/VBox/Devices/Graphics/DevVGA.cpp -@@ -6217,6 +6217,49 @@ static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) - # endif - } - -+template -+auto generateDummy(int (*CLASS::*)(ARGS...)) -+{ -+ return [](ARGS...) -> int { return VERR_NOT_IMPLEMENTED; }; -+} -+ -+template -+auto generateDummy(void (*CLASS::*)(ARGS...)) -+{ -+ return [](ARGS...) -> void {}; -+} -+ -+#define DUMMY_MEMBER(x) dummyConnector.x = generateDummy(&PDMIDISPLAYCONNECTOR::x) -+ -+/** -+ * Get a dummy display connector which either does nothing or returns an error -+ * code. -+ */ -+static PPDMIDISPLAYCONNECTOR getDummyDisplayConnector() -+{ -+ static PDMIDISPLAYCONNECTOR dummyConnector; -+ -+ DUMMY_MEMBER(pfnResize); -+ DUMMY_MEMBER(pfnUpdateRect); -+ DUMMY_MEMBER(pfnRefresh); -+ DUMMY_MEMBER(pfnReset); -+ DUMMY_MEMBER(pfnLFBModeChange); -+ DUMMY_MEMBER(pfnProcessAdapterData); -+ DUMMY_MEMBER(pfnProcessDisplayData); -+ DUMMY_MEMBER(pfnVHWACommandProcess); -+ DUMMY_MEMBER(pfnVBVAEnable); -+ DUMMY_MEMBER(pfnVBVADisable); -+ DUMMY_MEMBER(pfnVBVAUpdateBegin); -+ DUMMY_MEMBER(pfnVBVAUpdateProcess); -+ DUMMY_MEMBER(pfnVBVAUpdateEnd); -+ DUMMY_MEMBER(pfnVBVAResize); -+ DUMMY_MEMBER(pfnVBVAMousePointerShape); -+ DUMMY_MEMBER(pfnVBVAGuestCapabilityUpdate); -+ DUMMY_MEMBER(pfnVBVAInputMappingUpdate); -+ DUMMY_MEMBER(pfnVBVAReportCursorPosition); -+ -+ return &dummyConnector; -+} - - /** - * @interface_method_impl{PDMDEVREG,pfnAttach} -@@ -6230,6 +6273,11 @@ static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t - - RT_NOREF(pThis); - -+ if (fFlags & PDM_ATTACH_DUMMY_DRIVER) { -+ pThisCC->pDrv = getDummyDisplayConnector(); -+ return VINF_SUCCESS; -+ } -+ - AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, - ("VGA device does not support hotplugging\n"), - VERR_INVALID_PARAMETER); -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpu.cpp b/src/VBox/Devices/Graphics/DevVirtioGpu.cpp -new file mode 100644 -index 0000000000..20f3e54de8 ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpu.cpp -@@ -0,0 +1,407 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_GPU -+#include "DevVirtioGpu.hpp" -+#include "DevVirtioGpuVBoxStubs.hpp" -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+#include -+ -+int VirtioGpuDevice::init(PPDMDEVINS pDevIns, int iInstance, uint32_t u32VRamSize, uint32_t cMonitorCount, -+ bool secondaryController) -+{ -+ std::string sInstanceName = std::string {"VIRTIOGPU"} + std::to_string(iInstance); -+ szInst = sInstanceName; -+ -+ int rc {VINF_SUCCESS}; -+ -+ gpuConfig.uNumScanouts = cMonitorCount; -+ -+ rc = initializeVirtio(pDevIns); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ rc = initializeVirtQueues(); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ rc = initializeDisplay(u32VRamSize, cMonitorCount); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ pMemoryAdapter = std::make_unique(virtio.pDevInsR3); -+ AssertLogRelReturn(pMemoryAdapter != nullptr, VERR_NO_MEMORY); -+ -+ pCmdHandler = std::make_unique(*pVirtioAdapter, *pDisplayManager, *pMemoryAdapter, -+ cMonitorCount, secondaryController); -+ AssertLogRelReturn(pCmdHandler != nullptr, VERR_NO_MEMORY); -+ -+ return rc; -+} -+ -+int VirtioGpuDevice::initializeVirtio(PPDMDEVINS pDevIns) -+{ -+ VIRTIOPCIPARAMS virtioPciParams; -+ virtioPciParams.uDeviceId = virtioGpu::PCI_DEVICE_ID; -+ virtioPciParams.uSubsystemId = -+ virtioGpu::PCI_DEVICE_ID; /* Virtio 1.2 - 4.1.2.1 subsystem id may reflect device id */ -+ virtioPciParams.uClassBase = virtioGpu::PCI_CLASS_BASE; -+ virtioPciParams.uClassSub = virtioGpu::PCI_CLASS_SUB; -+ virtioPciParams.uClassProg = virtioGpu::PCI_CLASS_PROG; -+ -+ virtioPciParams.uInterruptLine = virtioGpu::PCI_INTERRUPT_LINE; -+ virtioPciParams.uInterruptPin = virtioGpu::PCI_INTERRUPT_PIN; -+ -+ PVIRTIOGPUDEVCC pThisCC {PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOGPUDEVCC)}; -+ -+ pThisCC->virtio.pfnStatusChanged = virtioGpuStatusChanged; -+ pThisCC->virtio.pfnDevCapRead = virtioGpuDevCapRead; -+ pThisCC->virtio.pfnDevCapWrite = virtioGpuDevCapWrite; -+ pThisCC->virtio.pfnVirtqNotified = virtioGpuVirtqNotified; -+ -+ int rc = virtioCoreR3Init(pDevIns, &this->virtio, &pThisCC->virtio, &virtioPciParams, szInst.c_str(), -+ FEATURES_OFFERED, 0, &this->gpuConfig, sizeof(gpuConfig)); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ pVirtioAdapter = std::make_unique(&virtio); -+ AssertLogRelReturn(pVirtioAdapter != nullptr, VERR_NO_MEMORY); -+ -+ return rc; -+} -+ -+int VirtioGpuDevice::initializeVirtQueues() -+{ -+ for (size_t uVirtqNbr {0u}; uVirtqNbr < virtioGpu::NUM_VIRTQUEUES; uVirtqNbr++) { -+ PVIRTIOGPU_VIRTQ pVirtq {&aVirtqs[uVirtqNbr]}; -+ PVIRTIOGPU_WORKER pWorker {&aWorkers[uVirtqNbr]}; -+ -+ int rc = RTSemEventCreate(&pWorker->hEvent); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ std::string sVirtqName {uVirtqNbr == virtioGpu::VirtqIdx::CONTROLQ ? std::string {"controlq"} -+ : std::string {"cursorq"}}; -+ pVirtq->szName = sVirtqName; -+ pVirtq->uIdx = uVirtqNbr; -+ pWorker->uIdx = uVirtqNbr; -+ } -+ return VINF_SUCCESS; -+} -+ -+int VirtioGpuDevice::initializeDisplay(uint32_t u32VRamSize, uint32_t u32MonitorCount) -+{ -+ PPDMDEVINS pDevIns {virtio.pDevInsR3}; -+ -+ pDevIns->IBase.pfnQueryInterface = virtioGpuQueryInterface; -+ IBase.pfnQueryInterface = virtioGpuPortQueryInterface; -+ IPort.pfnUpdateDisplay = virtioGpuUpdateDisplay; -+ -+ IPort.pfnUpdateDisplayAll = virtioGpuPortUpdateDisplayAll; -+ IPort.pfnQueryVideoMode = virtioGpuPortQueryVideoMode; -+ IPort.pfnSetRefreshRate = virtioGpuPortSetRefreshRate; -+ IPort.pfnTakeScreenshot = virtioGpuPortTakeScreenshot; -+ IPort.pfnFreeScreenshot = virtioGpuPortFreeScreenshot; -+ IPort.pfnDisplayBlt = virtioGpuPortDisplayBlt; -+ IPort.pfnUpdateDisplayRect = virtioGpuPortUpdateDisplayRect; -+ IPort.pfnCopyRect = virtioGpuPortCopyRect; -+ IPort.pfnSetRenderVRAM = virtioGpuPortSetRenderVRAM; -+ // used for SVGA only -+ IPort.pfnSetViewport = NULL; -+ IPort.pfnSendModeHint = vbvavirtioGpuPortSendModeHint; -+ IPort.pfnReportHostCursorCapabilities = vbvavirtioGpuPortReportHostCursorCapabilities; -+ IPort.pfnReportHostCursorPosition = vbvavirtioGpuPortReportHostCursorPosition; -+ -+ IVirtioGpuPort.pfnDisplayChanged = virtioGpuDisplayChanged; -+ -+ pDisplayManager = -+ std::make_unique(pDevIns, 0 /*iLUN*/, IBase, u32VRamSize, u32MonitorCount); -+ AssertLogRelReturn(pDisplayManager != nullptr, VERR_NO_MEMORY); -+ return VINF_SUCCESS; -+} -+ -+int VirtioGpuDevice::terminate(PPDMDEVINS) -+{ -+ int rc {VINF_SUCCESS}; -+ -+ stop(); -+ for (size_t uVirtqNbr {0u}; uVirtqNbr < virtioGpu::NUM_VIRTQUEUES; uVirtqNbr++) { -+ rc = RTSemEventDestroy(aWorkers[uVirtqNbr].hEvent); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ } -+ -+ pCmdHandler.reset(); -+ pVirtioAdapter.reset(); -+ pDisplayManager.reset(); -+ pMemoryAdapter.reset(); -+ -+ return rc; -+} -+ -+int VirtioGpuDevice::start() -+{ -+ fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&virtio); -+ return startVirtQueues(); -+} -+ -+int VirtioGpuDevice::stop() -+{ -+ int rc {stopVirtQueues()}; -+ -+ gpuConfig.uEventsRead = 0; -+ gpuConfig.uEventsClear = 0; -+ -+ pCmdHandler->clearResources(); -+ return rc; -+} -+ -+/* -+ * virtioMmioRead and virtioMmioWrite both return VINF_IOM_MMIO_UNUSED_00 in case -+ * of a bad access, thus we use this return value too. -+ */ -+ -+int VirtioGpuDevice::accessCap(uint32_t uOffset, std::function accessFn) -+{ -+ switch (uOffset) { -+ case 0: accessFn(&gpuConfig.uEventsRead); return VINF_SUCCESS; -+ case 4: -+ accessFn(&gpuConfig.uEventsClear); -+ /* -+ * uEventsRead has write-to-clear semantics, i.e. when the driver writes -+ * a bit to uEventsClear, we clear the bit in uEventsRead and clear uEventsClear -+ */ -+ gpuConfig.uEventsRead &= ~gpuConfig.uEventsClear; -+ gpuConfig.uEventsClear = 0u; -+ return VINF_SUCCESS; -+ case 8: accessFn(&gpuConfig.uNumScanouts); return VINF_SUCCESS; -+ case 12: accessFn(&gpuConfig.uNumCapsets); return VINF_SUCCESS; -+ default: -+ LogRel(("%s: Invalid offset while accessing capabilties: %u\n", szInst.c_str(), uOffset)); -+ return VINF_IOM_MMIO_UNUSED_00; -+ } -+} -+ -+/* -+ * Virtio 1.2 - 4.1.3.1: For device configuration access, the driver MUST use [...] -+ * 32-bit wide and aligned accesses for 32-bit and 64-bit wide fields. -+ */ -+int VirtioGpuDevice::readCap(uint32_t uOffset, void* pvBuf, uint32_t cbToRead) -+{ -+ if (pvBuf == nullptr) { -+ LogRel(("%s: readCap: buffer to write to is a nullptr.\n", szInst.c_str())); -+ return VINF_IOM_MMIO_UNUSED_00; -+ } -+ if (cbToRead != sizeof(uint32_t)) { -+ LogRel(("%s: readCap: invalid access size. Tried to read %u bytes.\n", szInst.c_str(), cbToRead)); -+ return VINF_IOM_MMIO_UNUSED_00; -+ } -+ -+ std::function accessFn = [pvBuf](uint32_t* pMember) { *static_cast(pvBuf) = *pMember; }; -+ -+ return accessCap(uOffset, accessFn); -+} -+ -+int VirtioGpuDevice::writeCap(uint32_t uOffset, const void* pvBuf, uint32_t cbToWrite) -+{ -+ if (pvBuf == nullptr) { -+ LogRel(("%s: writeCap: buffer to write to is a nullptr.\n", szInst.c_str())); -+ return VINF_IOM_MMIO_UNUSED_00; -+ } -+ if (cbToWrite != sizeof(uint32_t)) { -+ LogRel(("%s: writeCap: invalid access size. Tried to write %u bytes.\n", szInst.c_str(), cbToWrite)); -+ return VINF_IOM_MMIO_UNUSED_00; -+ } -+ if (uOffset != 4) { -+ /* the driver is only allowed to write to uEventsClear */ -+ LogRel(("%s: writeCap: invalid access: the driver may only write to offset 4 (offset was %u).\n", -+ szInst.c_str(), uOffset)); -+ return VINF_IOM_MMIO_UNUSED_00; -+ } -+ -+ std::function accessFn = [pvBuf](uint32_t* pMember) { -+ *pMember = *static_cast(pvBuf); -+ }; -+ -+ return accessCap(uOffset, accessFn); -+} -+ -+void VirtioGpuDevice::displayChanged(uint32_t numDisplays, VMMDevDisplayDef* displayDefs) -+{ -+ for (uint32_t i = 0; i < numDisplays; i++) { -+ bool enabled {not(displayDefs[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)}; -+ -+ pCmdHandler->requestResize(i, enabled, displayDefs[i].cx, displayDefs[i].cy); -+ } -+ -+ gpuConfig.uEventsRead |= virtioGpu::EVENT_DISPLAY; -+ LogRel5(("%s: device configuration has changed.\n", szInst.c_str())); -+ -+ virtioCoreNotifyConfigChanged(&virtio); -+} -+ -+void VirtioGpuDevice::wakeupWorker(uint16_t uVirtqNbr) -+{ -+ if (uVirtqNbr != virtioGpu::VirtqIdx::CONTROLQ and uVirtqNbr != virtioGpu::VirtqIdx::CURSORQ) { -+ LogRel(("%s: tried to wake up unrecognized queue number: %d.\n", szInst.c_str(), uVirtqNbr)); -+ return; -+ } -+ -+ PVIRTIOGPU_WORKER pWorker {&aWorkers[uVirtqNbr]}; -+ -+ if (not ASMAtomicXchgBool(&pWorker->fNotified, true)) { -+ /* -+ * Two atomic variables to avoid (at least some) unnecessary signals. -+ * The fNotified variable should suffice to avoid lost signals. -+ */ -+ if (ASMAtomicReadBool(&pWorker->fSleeping)) { -+ int rc {RTSemEventSignal(pWorker->hEvent)}; -+ AssertRC(rc); -+ } -+ } -+} -+ -+template -+static DECLCALLBACK(int) virtQueueHandleFn(RTTHREAD /*hSelf*/, void* pvUser) -+{ -+ PVIRTIOGPUDEV pThis {static_cast(pvUser)}; -+ return pThis->handleVirtQueue(uVirtqNbr); -+} -+ -+auto controlQueueHandleFn {virtQueueHandleFn}; -+auto cursorQueueHandleFn {virtQueueHandleFn}; -+ -+int VirtioGpuDevice::startVirtQueues() -+{ -+ fTerminateVirtQueues.store(false); -+ -+ for (size_t uVirtqNbr {0u}; uVirtqNbr < virtioGpu::NUM_VIRTQUEUES; uVirtqNbr++) { -+ PVIRTIOGPU_VIRTQ pVirtq {&aVirtqs[uVirtqNbr]}; -+ PVIRTIOGPU_WORKER pWorker {&aWorkers[uVirtqNbr]}; -+ -+ auto handlerFn = uVirtqNbr == virtioGpu::VirtqIdx::CONTROLQ ? controlQueueHandleFn : cursorQueueHandleFn; -+ int rc {RTThreadCreate(&pWorker->hThread, handlerFn, this, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, -+ pVirtq->szName.c_str())}; -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ pWorker->fAssigned = true; -+ -+ rc = virtioCoreR3VirtqAttach(&virtio, pVirtq->uIdx, pVirtq->szName.c_str()); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ virtioCoreVirtqEnableNotify(&virtio, pVirtq->uIdx, true); -+ pVirtq->fAttachedToVirtioCore = true; -+ } -+ -+ return VINF_SUCCESS; -+} -+ -+int VirtioGpuDevice::stopVirtQueues() -+{ -+ fTerminateVirtQueues.store(true); -+ -+ for (size_t uVirtqNbr {0u}; uVirtqNbr < virtioGpu::NUM_VIRTQUEUES; uVirtqNbr++) { -+ PVIRTIOGPU_VIRTQ pVirtq {&aVirtqs[uVirtqNbr]}; -+ PVIRTIOGPU_WORKER pWorker {&aWorkers[uVirtqNbr]}; -+ -+ if (not pWorker->fAssigned) { -+ continue; -+ } -+ -+ int rc = RTSemEventSignal(pWorker->hEvent); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ rc = RTThreadWaitNoResume(pWorker->hThread, RT_INDEFINITE_WAIT, nullptr); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ pWorker->fAssigned = false; -+ pVirtq->fAttachedToVirtioCore = false; -+ } -+ -+ return VINF_SUCCESS; -+} -+ -+int VirtioGpuDevice::handleVirtQueue(uint16_t uVirtqNbr) -+{ -+ PVIRTIOGPU_VIRTQ pVirtq {&aVirtqs[uVirtqNbr]}; -+ PVIRTIOGPU_WORKER pWorker {&aWorkers[uVirtqNbr]}; -+ PPDMDEVINS pDevIns {virtio.pDevInsR3}; -+ -+ LogRel2(("%s: worker thread %d started for %s (virtq idx=%d).\n", szInst.c_str(), pWorker->uIdx, -+ pVirtq->szName.c_str(), pVirtq->uIdx)); -+ -+ auto is_virtq_empty = [this, &pDevIns, uVirtqNbr]() -> bool { -+ return virtioCoreVirtqAvailBufCount(pDevIns, &virtio, uVirtqNbr) == 0; -+ }; -+ -+ while (not fTerminateVirtQueues.load()) { -+ if (is_virtq_empty()) { -+ /* -+ * Two atomic variables to avoid (at least some) unnecessary signals. -+ * The fNotified variable should suffice to avoid lost signals. -+ */ -+ ASMAtomicWriteBool(&pWorker->fSleeping, true); -+ if (not ASMAtomicXchgBool(&pWorker->fNotified, false)) { -+ int rc {RTSemEventWait(pWorker->hEvent, RT_INDEFINITE_WAIT)}; -+ AssertLogRelReturn(RT_SUCCESS(rc) or rc == VERR_INTERRUPTED, rc); -+ -+ if (rc == VERR_INTERRUPTED) { -+ continue; -+ } -+ -+ ASMAtomicWriteBool(&pWorker->fNotified, false); -+ } -+ ASMAtomicWriteBool(&pWorker->fSleeping, false); -+ } -+ -+ if (RT_UNLIKELY(fTerminateVirtQueues.load())) { -+ break; -+ } -+ -+ if (is_virtq_empty()) { -+ /* -+ * It may happen that we got an unnecessary signal, thus we double-check -+ * wether the virtq is empty. -+ */ -+ continue; -+ } -+ -+#ifdef VIRTIO_VBUF_ON_STACK -+ PVIRTQBUF pVirtqBuf = virtioCoreR3VirtqBufAlloc(); -+ if (!pVirtqBuf) { -+ LogRel(("Failed to allocate memory for VIRTQBUF\n")); -+ break; /* No point in trying to allocate memory for other descriptor -+ chains */ -+ } -+ int rc {virtioCoreR3VirtqAvailBufGet(pDevIns, &virtio, pVirtq->uIdx, pVirtqBuf, true)}; -+#else -+ // The virtq is not empty, we take a buffer from it and handle it. -+ PVIRTQBUF pVirtqBuf {nullptr}; -+ int rc {virtioCoreR3VirtqAvailBufGet(pDevIns, &virtio, pVirtq->uIdx, &pVirtqBuf, true)}; -+#endif -+ -+ if (rc == VERR_NOT_AVAILABLE) { -+ continue; -+ } -+ -+ pCmdHandler->handleBuffer(pVirtqBuf); -+ virtioCoreR3VirtqBufRelease(&virtio, pVirtqBuf); -+ } -+ -+ return VINF_SUCCESS; -+} -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpu.hpp b/src/VBox/Devices/Graphics/DevVirtioGpu.hpp -new file mode 100644 -index 0000000000..72474ed6eb ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpu.hpp -@@ -0,0 +1,373 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#pragma once -+ -+#include -+ -+#include -+#include -+#include -+ -+#include "../VirtIO/VirtioCore.h" -+#include "DevVirtioGpuDefinitions.hpp" -+#include "DevVirtioGpuDisplayManager.hpp" -+#include "DevVirtioGpuCmdHandler.hpp" -+#include "DevVirtioGpuResource.hpp" -+ -+#include -+#include -+#include -+#include -+#include -+ -+class VirtioCoreVirtioAdapter; -+class VirtioGpuMemoryAdapter; -+ -+/** -+ * Logging-Level rules for anything inside LOG_GROUP_DEV_VIRTIO_GPU: -+ * LogRel - conditions that lead to functions returning early or returning anything other than VINF_SUCCESS -+ * LogRel2 - logging of things that should only happen once or very few, e.g. creation of a queue or taking over the -+ * driver LogRel5 - informative logging from the virtio-gpu (only DevVirtioGpu.hpp and DevVirtioGpu.cpp, but not the -+ * adapters) LogRel6 - informative logging from any adapter LogRel7 - informative logging from the cmd-handler LogRel8 - -+ * informative logging inside the VBox-Stubs (DevVirtioGpuVBoxStubs.hpp and DevVirtioGpuVBoxStubs.cpp) -+ * -+ * Enabling logging levels 1 and 2 shouldn't lead to too much output (common sense applies), while the other logging -+ * levels may lead to a lot of output. -+ * -+ * If you have access to VirtioGpuDevice::szInst, start your messages with ("%s: ...", virtioGpuDevice.szInst.c_str()). -+ * Otherwise start your messages with a prefix for easy grepping. -+ * -+ * Enable logging for only the virtio-gpu with: 'export VBOX_RELEASE_LOG="-all+dev_virtio_gpu.e.lA.lB" where A and B are -+ * the desired logging levels. You can of course add more logging levels with ".lC.lD...". ".e" automatically enables -+ * logging level 1, i.e. LogRel. -+ * -+ */ -+ -+class VirtioGpuDevice -+{ -+ static constexpr uint16_t FEATURES_OFFERED {virtioGpu::Features::EDID}; ///< The features offered to the guest -+ /// -+public: -+ /** -+ * Initialize the Virtio GPU -+ * -+ * \param pDevIns The PCI Device Instance -+ * \param iInstance The instance number. -+ * \param u32VRamSize The size of the VRam. -+ * \param cMonitorCount The amount of displays configured for the VM. -+ * \param secondaryController True if this is a secondary graphics controller, e.g. if the active graphics -+ * controller is VGAWithVirtioGpu, false if this is the only graphics controller. -+ * -+ * \return VBox status code -+ */ -+ int init(PPDMDEVINS pDevIns, int iInstance, uint32_t u32VRamSize, uint32_t cMonitorCount, bool secondaryController); -+ -+ /** -+ * Terminates the Virtio GPU. -+ * -+ * \param pDevIns The PCI Device Instance -+ * -+ * \return VBox status code -+ */ -+ int terminate(PPDMDEVINS pDevIns); -+ -+ /** -+ * Start the Virtio GPU. This function is called when the driver calls -+ * pfnStatusChanged with fDriverOk != 0. -+ * -+ * \return VBox Status Code -+ */ -+ int start(); -+ -+ /** -+ * Stop the Virtio GPU. This function is called when the driver calls -+ * pfnStatusChanged with fDriverOk == 0. -+ * -+ * \return VBox Status Code -+ */ -+ int stop(); -+ -+ /** -+ * Read from the device-specific configuration. -+ * -+ * \param uOffset The offset into the device-specific configuration -+ * \param pvBuf The buffer in which to save the read data -+ * \param cbToRead The number of bytes to read -+ * -+ * \return VBox Status Code -+ */ -+ int readCap(uint32_t uOffset, void* pvBuf, uint32_t cbToRead); -+ -+ /** -+ * Write to the device-specific configuration. -+ * -+ * \param uOffset The offset into the device-specific configuration -+ * \param pvBuf The buffer with the bytes to write -+ * \param cbToWrite The number of bytes to write -+ * -+ * \return VBox Status Code -+ */ -+ int writeCap(uint32_t uOffset, const void* pvBuf, uint32_t cbToWrite); -+ -+ /** -+ * Informs the worker of a virtqueue that it has new buffers. -+ * -+ * \param uVirtqNbr The number of the virtqueue that has new buffers. -+ */ -+ void wakeupWorker(uint16_t uVirtqNbr); -+ -+ /** -+ * The handler function for the virtqueues. -+ * -+ * \param uVirtqNbr The index of the associcated virtqueue -+ * \param pDevIns The PCI Device Instance -+ * -+ * \return VBox status code -+ */ -+ int handleVirtQueue(uint16_t uVirtqNbr); -+ -+private: -+ /** -+ * Initialize the Virtio-Core part of the Virtio GPU -+ * -+ * \param pDevIns The PCI Device Instance -+ * -+ * \return VBox status code -+ */ -+ int initializeVirtio(PPDMDEVINS pDevIns); -+ -+ /** -+ * Initialize the virtqueues, but do NOT start them. -+ * -+ * \return VBox status code. -+ */ -+ int initializeVirtQueues(); -+ -+ /** -+ * Initializes the display, i.e. assigns functions to the driver etc. -+ * -+ * \param u32VRamSize The size of the VRam -+ * \param u32MonitorCount The maximum of attachable monitors -+ * -+ * \return VBox status code. -+ */ -+ int initializeDisplay(uint32_t u32VRamSize, uint32_t u32MonitorCount); -+ -+ /** -+ * Start the virtqueues, i.e. start the worker-threads and attach the -+ * virtqueues to virtio core. -+ * -+ * \return VBox status code -+ */ -+ int startVirtQueues(); -+ -+ /** -+ * Stop the virtqueues, i.e. stop the worker-threads. -+ * -+ * \return VBox status code -+ */ -+ int stopVirtQueues(); -+ -+ /** -+ * Accesses the device-specific configuration at the given offset using the -+ * given function. -+ * -+ * \param uOffset The offset into the device-specific configuration -+ * \param accessFn The function that is used to access the device-specific configuration -+ * -+ * \return VBox status code -+ */ -+ int accessCap(uint32_t uOffset, std::function accessFn); -+ -+public: -+ /* device-specific queue info */ -+ typedef struct VirtioGpuVirtQueue -+ { -+ uint16_t uIdx; ///< The index of this virtqueue -+ uint16_t uPadding; -+ std::string szName; ///< The name of this virtqueue -+ bool fHasWorker; ///< If set this virtqueue has an associated worker -+ bool fAttachedToVirtioCore; ///< If set this virtqueue is attached to virtio core -+ } VIRTIOGPU_VIRTQ, *PVIRTIOGPU_VIRTQ; -+ -+ /* a worker thread of a virtqueue */ -+ typedef struct VirtioGpuWorker -+ { -+ RTSEMEVENT hEvent; ///< The handle of the associated sleep/wake-up semaphore -+ RTTHREAD hThread; ///< The handle of the associated worker-thread -+ uint16_t uIdx; ///< The index of this worker (should be the same as the index of the associated virtq) -+ bool volatile fSleeping; ///< If set this thread is sleeping -+ bool volatile fNotified; ///< If set this thread has been notified that there is work to do -+ bool fAssigned; ///< If set this thread has been set up -+ } VIRTIOGPU_WORKER, *PVIRTIOGPU_WORKER; -+ -+public: -+ /* virtio core requires that members are public.*/ -+ VIRTIOCORE virtio; // core virtio state -+ virtioGpu::Config gpuConfig; // device specific configuration of the virtio GPU -+ -+ VirtioGpuVirtQueue aVirtqs[virtioGpu::NUM_VIRTQUEUES]; -+ VirtioGpuWorker aWorkers[virtioGpu::NUM_VIRTQUEUES]; -+ std::atomic fTerminateVirtQueues; -+ -+ std::string szInst; // instance name -+ -+ uint64_t fNegotiatedFeatures; // features negotiated with the guest -+ -+ /* The commands send by the driver are handled by the VirtioGpuCmdHandler. To be able -+ * to test this class using unit-tests, the handler needs a few adapters to be able to control -+ * how pages are mapped, displays are handled and the commands are read. -+ */ -+ std::unique_ptr pCmdHandler; -+ std::unique_ptr pVirtioAdapter; -+ std::unique_ptr pDisplayManager; -+ std::unique_ptr pMemoryAdapter; -+ -+public: -+ PDMIBASE IBase; -+ PDMIDISPLAYPORT IPort; -+ PDMIDISPLAYVBVACALLBACKS IVBVACallbacks; -+ PDMIVIRTIOGPUPORT IVirtioGpuPort; -+ -+ /** -+ * Signals to the driver that the resolution or the monitor status -+ * (enabled, disabled) has changed. -+ * -+ * \param numDisplays Number of displays available -+ * \param displayDefs Array of display definitions describing the -+ * configuration of each display -+ */ -+ void displayChanged(uint32_t numDisplays, VMMDevDisplayDef* displayDefs); -+ -+ /** -+ * Attaches the Virtio-GPU to the VBox-window. -+ * -+ * \param iLUN The LUN to attach to. This must be 0. -+ * -+ * \return VBox status code -+ */ -+ int attachDisplay(unsigned iLUN); -+ -+ /** -+ * Detaches the Virtio-GPU driver. -+ * -+ * \param iLUN The LUN to detach from. -+ */ -+ void detachDisplay(unsigned iLUN); -+}; -+ -+/** -+ * VirtioCore needs a separate class that holds the R3 state -+ */ -+class VirtioGpuDeviceR3 -+{ -+public: -+ VIRTIOCORER3 virtio; // core virtio state R3 -+}; -+ -+typedef VirtioGpuDevice VIRTIOGPUDEV; -+typedef VIRTIOGPUDEV* PVIRTIOGPUDEV; -+ -+typedef VirtioGpuDeviceR3 VIRTIOGPUDEVCC; -+typedef VIRTIOGPUDEVCC* PVIRTIOGPUDEVCC; -+ -+class VirtioCoreVirtioAdapter final : public VirtioGpuCmdHandler::VirtioAdapter -+{ -+ PVIRTIOCORE pVirtio_ {nullptr}; -+ -+public: -+ VirtioCoreVirtioAdapter() = delete; -+ VirtioCoreVirtioAdapter(PVIRTIOCORE pVirtio) : pVirtio_(pVirtio) {} -+ -+ void virtqBufDrain(PVIRTQBUF pVirtqBuf, void* pv, size_t cb) final -+ { -+ virtioCoreR3VirtqBufDrain(pVirtio_, pVirtqBuf, pv, cb); -+ } -+ -+ void virtqBufPut(PVIRTQBUF pVirtqBuf, void* pv, size_t cb) final -+ { -+ PRTSGSEG pReturnSeg = static_cast(RTMemAllocZ(sizeof(RTSGSEG))); -+ AssertReleaseMsg(pReturnSeg != nullptr, ("Out of memory")); -+ -+ pReturnSeg->pvSeg = RTMemAllocZ(cb); -+ AssertReleaseMsg(pReturnSeg->pvSeg != nullptr, ("Out of memory")); -+ memcpy(pReturnSeg->pvSeg, pv, cb); -+ pReturnSeg->cbSeg = cb; -+ -+ PRTSGBUF pReturnSegBuf = static_cast(RTMemAllocZ(sizeof(RTSGBUF))); -+ AssertReleaseMsg(pReturnSegBuf, ("Out of memory")); -+ -+ RTSgBufInit(pReturnSegBuf, pReturnSeg, 1); -+ -+ virtioCoreR3VirtqUsedBufPut(pVirtio_->pDevInsR3, pVirtio_, pVirtqBuf->uVirtq, pReturnSegBuf, pVirtqBuf, -+ true /* fFence */); -+ -+ RTMemFree(pReturnSeg->pvSeg); -+ RTMemFree(pReturnSeg); -+ RTMemFree(pReturnSegBuf); -+ } -+ -+ void virtqSyncRings(PVIRTQBUF pVirtqBuf) final -+ { -+ virtioCoreVirtqUsedRingSync(pVirtio_->pDevInsR3, pVirtio_, pVirtqBuf->uVirtq); -+ } -+}; -+ -+class VirtioGpuMemoryAdapter final : public VirtioGpuCmdHandler::MemoryAdapter -+{ -+ PPDMDEVINS pDevIns_ {nullptr}; -+ -+public: -+ VirtioGpuMemoryAdapter() = delete; -+ VirtioGpuMemoryAdapter(PPDMDEVINS pDevIns) : pDevIns_(pDevIns) {} -+ -+ VecMappings mapGCPhys2HCVirt(const VecMemEntries& vBacking) -+ { -+ VecMappings vMapping; -+ vMapping.reserve(vBacking.size()); -+ -+ for (const auto& backing : vBacking) { -+ size_t currSize {backing.uLength_}; -+ /* -+ * PDMDevHlpPhysGCPhys2CCPtr always maps exactly one page, thus it may -+ * happen that we need multiple mappings for one backing-entry -+ */ -+ while (currSize != 0) { -+ PPGMPAGEMAPLOCK pLock {new PGMPAGEMAPLOCK}; -+ void* vAddr {nullptr}; -+ int rc {PDMDevHlpPhysGCPhys2CCPtr(pDevIns_, backing.uAddr_, 0, &vAddr, pLock)}; -+ AssertRC(rc); -+ vMapping.emplace_back(vAddr, PAGE_SIZE, pLock); -+ currSize -= PAGE_SIZE; -+ } -+ } -+ -+ return vMapping; -+ } -+ -+ void releaseMappings(const VecMappings& vMapping) -+ { -+ for (const auto& mapping : vMapping) { -+ PPGMPAGEMAPLOCK pLock {reinterpret_cast(mapping.pv_)}; -+ PDMDevHlpPhysReleasePageMappingLock(pDevIns_, pLock); -+ delete pLock; -+ } -+ } -+}; -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpuCmdHandler.cpp b/src/VBox/Devices/Graphics/DevVirtioGpuCmdHandler.cpp -new file mode 100644 -index 0000000000..4db05af5b7 ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpuCmdHandler.cpp -@@ -0,0 +1,687 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_GPU -+#include "DevVirtioGpuCmdHandler.hpp" -+ -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+VirtioGpuCmdHandler::VirtioGpuCmdHandler(VirtioAdapter& vAdapter, DisplayManager& dManager, MemoryAdapter& mAdapter, -+ uint32_t numScanouts, bool attachDisplayLater) -+ : virtioAdapter_(vAdapter), displayManager_(dManager), memoryAdapter_(mAdapter), numScanouts_(numScanouts) -+{ -+ for (unsigned currentScanout {0u}; currentScanout < numScanouts_; currentScanout++) { -+ Scanout scanout {displayManager_}; -+ scanout.uScanoutId = currentScanout; -+ -+ /* if this is the only graphics controller we want to attach immediately to the display. */ -+ if (not attachDisplayLater and scanout.hasDisplay() and not scanout.isAttachedToDisplay()) { -+ scanout.attachDisplay(); -+ } -+ -+ if (scanout.hasDisplay()) { -+ auto [uWidth, uHeight] = scanout.isAttachedToDisplay() ? scanout.displayDimensions() : getDummySize(); -+ scanout.uCurrentWidth = uWidth; -+ scanout.uCurrentHeight = uHeight; -+ } -+ -+ activeScanouts.push_back(scanout); -+ } -+ -+ LogRel2(("virtio-gpu cmd handler: created. Num of scanouts is %u.\n", activeScanouts.size())); -+} -+ -+inline bool VirtioGpuCmdHandler::scanoutExists(uint32_t uScanout) -+{ -+ return uScanout <= numScanouts_ - 1; -+} -+ -+inline std::optional VirtioGpuCmdHandler::getScanout(uint32_t uScanout) -+{ -+ if (not scanoutExists(uScanout)) { -+ return {}; -+ } -+ -+ return activeScanouts.at(uScanout); -+} -+ -+inline std::vector VirtioGpuCmdHandler::getScanoutsByResource(uint32_t uResourceId) -+{ -+ std::vector results; -+ for (auto& scanout : activeScanouts) { -+ if (not(scanout.uResourceId == uResourceId)) { -+ continue; -+ } -+ -+ results.emplace_back(std::ref(scanout)); -+ } -+ -+ return results; -+} -+ -+std::optional VirtioGpuCmdHandler::getCScanout(uint32_t uScanout) -+{ -+ auto maybeScanout {getScanout(uScanout)}; -+ -+ if (not maybeScanout.has_value()) { -+ return {}; -+ } -+ -+ return {std::cref(maybeScanout->get())}; -+} -+ -+void VirtioGpuCmdHandler::requestResize(uint32_t uScanout, bool enabled, uint32_t uWidth, uint32_t uHeight) -+{ -+ auto maybeScanout {getScanout(uScanout)}; -+ if (not maybeScanout.has_value()) { -+ LogRel(("virtio-gpu cmd handler: Scanout %d not available\n", uScanout)); -+ return; -+ } -+ -+ auto& currentScanout {maybeScanout->get()}; -+ -+ currentScanout.fActive = enabled; -+ if (!enabled) { -+ currentScanout.detachDisplay(); -+ } -+ -+ currentScanout.uResizedWidth = uWidth; -+ currentScanout.uResizedHeight = uHeight; -+ currentScanout.fResizeRequested = true; -+} -+ -+inline void VirtioGpuCmdHandler::resizeScanout(uint32_t uScanout, uint32_t uWidth, uint32_t uHeight) -+{ -+ auto maybeScanout {getScanout(uScanout)}; -+ if (not maybeScanout.has_value()) { -+ return; -+ } -+ -+ auto& currentScanout {maybeScanout->get()}; -+ if (uWidth != currentScanout.uCurrentWidth or uHeight != currentScanout.uCurrentHeight -+ or currentScanout.fNeedsResize) { -+ currentScanout.uCurrentWidth = uWidth; -+ currentScanout.uCurrentHeight = uHeight; -+ -+ if (currentScanout.isAttachedToDisplay()) { -+ currentScanout.fNeedsResize = false; -+ currentScanout.resizeDisplay(); -+ } -+ } -+} -+ -+inline VirtioGpuResource* VirtioGpuCmdHandler::getResource(uint32_t uResourceId) -+{ -+ auto result {std::find_if(std::begin(vResources_), std::end(vResources_), -+ [uResourceId](const auto& it) { return uResourceId == it->resourceId(); })}; -+ -+ return result == std::end(vResources_) ? nullptr : result->get(); -+} -+ -+inline bool VirtioGpuCmdHandler::createResource(uint32_t uResourceId) -+{ -+ if (getResource(uResourceId) != nullptr) { -+ return false; -+ } -+ -+ vResources_.emplace_back(new VirtioGpuResource(uResourceId)); -+ return true; -+} -+ -+inline void VirtioGpuCmdHandler::removeResource(uint32_t uResourceId) -+{ -+ auto result {std::find_if(std::begin(vResources_), std::end(vResources_), -+ [uResourceId](const auto& it) { return uResourceId == it->resourceId(); })}; -+ -+ if (result != std::end(vResources_)) { -+ vResources_.erase(result); -+ } -+ -+ auto scanouts {getScanoutsByResource(uResourceId)}; -+ for (auto& scanout : scanouts) { -+ scanout.get().uResourceId = 0; -+ } -+} -+ -+void VirtioGpuCmdHandler::handleBuffer(PVIRTQBUF pVirtqBuf) -+{ -+ if (pVirtqBuf->cbPhysSend < sizeof(virtioGpu::CtrlHdr)) { -+ LogRel(("virtio-gpu cmd handler: handleBuffer: request buffer of command in virtq %u too small\n", -+ pVirtqBuf->uVirtq)); -+ returnResponseNoData(pVirtqBuf, nullptr, virtioGpu::CtrlType::Response::ERR_OUT_OF_MEMORY); -+ return; -+ } -+ -+ /* -+ * This lock is a precaution to avoid race conditions. If done right, there are never more than two threads calling -+ * this function, and those two threads shouldn't interfere even if they call this function at the same time. -+ */ -+ RTCLock guard(mutex_); -+ -+ virtioGpu::CtrlHdr hdr; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, &hdr, sizeof(hdr)); -+ -+ switch (static_cast(hdr.uType)) { -+ case virtioGpu::CtrlType::Cmd::GET_DISPLAY_INFO: cmdGetDisplayInfo(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::GET_EDID: cmdGetEdid(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::RESOURCE_CREATE_2D: cmdResourceCreate2d(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::RESOURCE_UNREF: cmdResourceUnref(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::SET_SCANOUT: cmdSetScanout(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::RESOURCE_FLUSH: cmdResourceFlush(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::TRANSFER_TO_HOST_2D: cmdTransferToHost2d(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::RESOURCE_ATTACH_BACKING: cmdResourceAttachBacking(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::RESOURCE_DETACH_BACKING: cmdResourceDetachBacking(pVirtqBuf, &hdr); break; -+ case virtioGpu::CtrlType::Cmd::UPDATE_CURSOR: -+ case virtioGpu::CtrlType::Cmd::MOVE_CURSOR: -+ if (pVirtqBuf->uVirtq != virtioGpu::VirtqIdx::CURSORQ) { -+ /* -+ * Not sure wether ERR_UNSPEC is the right thing here, but this is -+ * also an odd error. -+ */ -+ returnResponseNoData(pVirtqBuf, &hdr, virtioGpu::CtrlType::Response::ERR_UNSPEC); -+ } else { -+ returnResponseNoData(pVirtqBuf, &hdr, virtioGpu::CtrlType::Response::OK_NODATA); -+ } -+ break; -+ default: -+ returnResponseNoData(pVirtqBuf, &hdr, virtioGpu::CtrlType::Response::ERR_UNSPEC); -+ LogRel(("virtio-gpu cmd handler: handleBuffer: got an unrecognized command in virtq %u: %#x\n", -+ pVirtqBuf->uVirtq, hdr.uType)); -+ } -+} -+ -+inline bool VirtioGpuCmdHandler::checkCtrlqCmd(const char* cmdName, PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr, -+ size_t cbSend, size_t cbReturn) -+{ -+ /* -+ * we subtract sizeof(virtioGpu::CtrlHdr) from cbSend, because we want to know -+ * wether we are able to drain to payload of a given command from pVirtqBuf. -+ * That way we can write e.g. sizeof(virtioGpu::getEdid) as the fourth argument, -+ * instead of writing sizeof(virtioGpu::getEdid) - sizeof(virtioGpu::CtrlHdr) -+ * every time -+ */ -+ cbSend = cbSend == 0 ? 0 : cbSend - sizeof(virtioGpu::CtrlHdr); -+ -+ if (pVirtqBuf->uVirtq != virtioGpu::VirtqIdx::CONTROLQ) { -+ LogRel(("virtio-gpu cmd handler: %s: command was in the wrong virtq.\n", cmdName)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_UNSPEC); -+ return false; -+ } -+ -+ if (cbSend > 0 and pVirtqBuf->cbPhysSend < cbSend) { -+ LogRel(("virtio-gpu cmd handler: %s: request buffer was too small.\n", cmdName)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_OUT_OF_MEMORY); -+ return false; -+ } -+ -+ if (cbReturn > 0 and pVirtqBuf->cbPhysReturn < cbReturn) { -+ LogRel(("virtio-gpu cmd handler: %s: response buffer was too small.\n", cmdName)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_OUT_OF_MEMORY); -+ return false; -+ } -+ -+ return true; -+} -+ -+inline bool VirtioGpuCmdHandler::checkScanoutId(const char* cmdName, PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr, -+ uint32_t uScanoutId) -+{ -+ auto maybeScanout {getScanout(uScanoutId)}; -+ if (not maybeScanout.has_value()) { -+ LogRel(("virtio-gpu cmd handler: %s: unknown scanout id %u\n", cmdName, uScanoutId)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_INVALID_SCANOUT_ID); -+ return false; -+ } -+ -+ auto& currentScanout {maybeScanout->get()}; -+ if (not currentScanout.hasDisplay()) { -+ LogRel(("virtio-gpu cmd handler: %s: scanout %u has no display.\n", cmdName, uScanoutId)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_INVALID_SCANOUT_ID); -+ return false; -+ } -+ -+ return true; -+} -+ -+inline bool VirtioGpuCmdHandler::checkResourceId(const char* cmdName, PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr, -+ uint32_t uResourceId) -+{ -+ if (getResource(uResourceId) == nullptr) { -+ LogRel(("virtio-gpu cmd handler: %s: resource id %u does not exist.\n", cmdName, uResourceId)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_INVALID_RESOURCE_ID); -+ return false; -+ } -+ return true; -+} -+ -+inline void VirtioGpuCmdHandler::returnResponseOkEarly(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not pCtrlHdr->has_flag(virtioGpu::CtrlHdr::Flags::FENCE)) { -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::OK_NODATA); -+ } -+} -+ -+inline void VirtioGpuCmdHandler::returnResponseOkLate(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (pCtrlHdr->has_flag(virtioGpu::CtrlHdr::Flags::FENCE)) { -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::OK_NODATA); -+ } -+} -+ -+inline void VirtioGpuCmdHandler::returnResponseNoData(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr, -+ virtioGpu::CtrlType::Response responseType) -+{ -+ if (pVirtqBuf->cbPhysReturn < sizeof(virtioGpu::CtrlHdr)) { -+ return; -+ } -+ -+ virtioGpu::CtrlHdr response {responseType}; -+ -+ if (pCtrlHdr != nullptr) { -+ /* -+ * It may happen that the caller of this functions passes a nullptr if -+ * the request buffer of pVirtqBuf is too small for a header. -+ */ -+ response.transfer_fence(pCtrlHdr); -+ } -+ -+ returnResponseBuf(pVirtqBuf, &response, sizeof(virtioGpu::CtrlHdr)); -+} -+ -+inline void VirtioGpuCmdHandler::returnResponseBuf(PVIRTQBUF pVirtqBuf, void* pv, size_t cb) -+{ -+ virtioAdapter_.virtqBufPut(pVirtqBuf, pv, cb); -+ virtioAdapter_.virtqSyncRings(pVirtqBuf); -+} -+ -+void VirtioGpuCmdHandler::cmdGetDisplayInfo(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("GetDisplayInfo", pVirtqBuf, pCtrlHdr, 0, -+ virtioGpu::ResponseDisplayInfo::size(numScanouts_))) { -+ return; -+ } -+ -+ LogRel7(("virtio-gpu cmd handler: Got GET_DISPLAY_INFO command.\n")); -+ virtioGpu::ResponseDisplayInfo response; -+ -+ for (unsigned i {0}; i < numScanouts_; i++) { -+ auto& pmode {response.pmodes[i]}; -+ if (scanoutExists(i)) { -+ auto& currentScanout {getScanout(i)->get()}; -+ -+ /* -+ * Here we should only report scanouts that are already attached to a -+ * display. But this doesn't work if a driver is started later, because -+ * then it wouldn't see any scanouts. -+ */ -+ if (not currentScanout.hasDisplay()) { -+ LogRel7(("virtio-gpu cmd handler: Scanout %u has no display.\n", i)); -+ continue; -+ } -+ -+ if (currentScanout.fResizeRequested) { -+ resizeScanout(i, currentScanout.uResizedWidth, currentScanout.uResizedHeight); -+ currentScanout.fResizeRequested = false; -+ } -+ -+ pmode.r.width = currentScanout.uCurrentWidth; -+ pmode.r.height = currentScanout.uCurrentHeight; -+ -+ pmode.enabled = currentScanout.fActive; -+ -+ continue; -+ } -+ } -+ -+ returnResponseBuf(pVirtqBuf, &response, virtioGpu::ResponseDisplayInfo::size(numScanouts_)); -+} -+ -+template -+static size_t sizeofPayload(VIRTIO_GPU_CMD cmd) -+{ -+ return sizeof(cmd) - sizeof(virtioGpu::CtrlHdr); -+} -+ -+void VirtioGpuCmdHandler::cmdGetEdid(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("GetEdid", pVirtqBuf, pCtrlHdr, sizeof(virtioGpu::GetEdid), -+ sizeof(virtioGpu::ResponseEdid))) { -+ return; -+ } -+ -+ virtioGpu::GetEdid request; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, request.payload(), sizeofPayload(request)); -+ LogRel7(("virtio-gpu cmd handler: Got GET_EDID command for scanout %u.\n", request.uScanout)); -+ -+ if (not checkScanoutId("GetEdid", pVirtqBuf, pCtrlHdr, request.uScanout)) { -+ return; -+ } -+ -+ virtioGpu::ResponseEdid response; -+ -+ auto& currentScanout {getScanout(request.uScanout)->get()}; -+ if (currentScanout.fResizeRequested) { -+ resizeScanout(0, currentScanout.uResizedWidth, currentScanout.uResizedHeight); -+ currentScanout.fResizeRequested = false; -+ } -+ -+ auto edid {generateExtendedEdid(currentScanout.uResizedWidth, currentScanout.uResizedHeight)}; -+ AssertReleaseMsg(sizeof(edid) <= sizeof(response.aEdid), -+ ("virtio-gpu cmd handler: GetEdid: Given EDID is too big to be returned to the driver!")); -+ response.uSize = std::min(sizeof(edid), sizeof(response.aEdid)); -+ std::memcpy(&response.aEdid, &edid, response.uSize); -+ -+ returnResponseBuf(pVirtqBuf, &response, sizeof(virtioGpu::ResponseEdid)); -+} -+ -+void VirtioGpuCmdHandler::cmdResourceCreate2d(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("ResourceCreate2D", pVirtqBuf, pCtrlHdr, sizeof(virtioGpu::ResourceCreate2d), -+ sizeof(virtioGpu::CtrlHdr))) { -+ return; -+ } -+ -+ virtioGpu::ResourceCreate2d request; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, request.payload(), sizeofPayload(request)); -+ LogRel7(("virtio-gpu cmd handler: Got RESOURCE_CREATE_2D command. (resource=%u, format=%u, width=%u, height=%u)\n", -+ request.uResourceId, request.uFormat, request.uWidth, request.uHeight)); -+ -+ if (request.uResourceId == 0) { -+ /* -+ * The driver can disable a scanout in SET_SCANOUT by setting uResourceId to 0. Thus -+ * (even though the specification doesn't say anything about this) we disallow creating -+ * resources with an Id of 0 here. -+ */ -+ LogRel(("virtio-gpu cmd handler: ResourceCreate2D: resource id %u can not be used.\n", request.uResourceId)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_INVALID_RESOURCE_ID); -+ return; -+ } -+ -+ if (not createResource(request.uResourceId)) { -+ LogRel(("virtio-gpu cmd handler: ResourceCreate2D: resource id %u already in use.\n", request.uResourceId)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_INVALID_RESOURCE_ID); -+ return; -+ } -+ -+ /* We currently only support the B8G8R8X8_UNORM pixel format. Thus, in case -+ * the driver uses another format, we print a message to the log. -+ * For some reason, the customers driver uses the B8G8R8A8_UNORM in the first -+ * resource it creates. Thus this format has to be enabled too. -+ */ -+ if ((request.uFormat & (virtioGpu::Format::B8G8R8A8_UNORM | virtioGpu::Format::B8G8R8X8_UNORM)) == 0) { -+ LogRel(("virtio-gpu cmd handler: ResourceCreate2D: An unsupported pixel-format has been set. This virtio-gpu " -+ "currently only supports B8G8R8X8_UNORM.\n")); -+ } -+ -+ returnResponseOkEarly(pVirtqBuf, pCtrlHdr); -+ -+ auto* resource {getResource(request.uResourceId)}; -+ resource->format(request.uFormat); -+ resource->size(request.uWidth, request.uHeight); -+ -+ returnResponseOkLate(pVirtqBuf, pCtrlHdr); -+} -+ -+void VirtioGpuCmdHandler::cmdResourceUnref(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("ResourceUnref", pVirtqBuf, pCtrlHdr, sizeof(virtioGpu::ResourceUnref), -+ sizeof(virtioGpu::CtrlHdr))) { -+ return; -+ } -+ -+ virtioGpu::ResourceUnref request; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, request.payload(), sizeofPayload(request)); -+ LogRel7(("virtio-gpu cmd handler: Got RESOURCE_UNREF command. (resource=%u)\n", request.uResourceId)); -+ -+ if (not checkResourceId("ResourceUnref", pVirtqBuf, pCtrlHdr, request.uResourceId)) { -+ return; -+ } -+ -+ returnResponseOkEarly(pVirtqBuf, pCtrlHdr); -+ -+ removeResource(request.uResourceId); -+ -+ returnResponseOkLate(pVirtqBuf, pCtrlHdr); -+} -+ -+void VirtioGpuCmdHandler::cmdSetScanout(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("SetScanout", pVirtqBuf, pCtrlHdr, sizeof(virtioGpu::SetScanout), -+ sizeof(virtioGpu::CtrlHdr))) { -+ return; -+ } -+ -+ virtioGpu::SetScanout request; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, request.payload(), sizeofPayload(request)); -+ LogRel7(("virtio-gpu cmd handler: Got SET_SCANOUT command. (scanout=%u, resource=%u, rect=w:%u,h:%u,x:%u,y:%u)\n", -+ request.uScanoutId, request.uResourceId, request.r.width, request.r.height, request.r.x, request.r.y)); -+ -+ if (not checkScanoutId("SetScanout", pVirtqBuf, pCtrlHdr, request.uScanoutId)) { -+ return; -+ } -+ -+ auto& currentScanout {getScanout(request.uScanoutId)->get()}; -+ if (request.uResourceId == 0) { -+ LogRel2(("virtio-gpu cmd handler: SetScanout: Driver disabled scanout %u\n", request.uScanoutId)); -+ currentScanout.fActive = false; -+ currentScanout.fNeedsResize = true; -+ currentScanout.detachDisplay(); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::OK_NODATA); -+ return; -+ } -+ -+ if (not checkResourceId("SetScanout", pVirtqBuf, pCtrlHdr, request.uResourceId)) { -+ return; -+ } -+ -+ returnResponseOkEarly(pVirtqBuf, pCtrlHdr); -+ -+ currentScanout.fActive = true; -+ currentScanout.uResourceId = request.uResourceId; -+ if (not currentScanout.isAttachedToDisplay()) { -+ currentScanout.attachDisplay(); -+ currentScanout.fNeedsResize = true; -+ } -+ -+ resizeScanout(request.uScanoutId, request.r.width, request.r.height); -+ -+ returnResponseOkLate(pVirtqBuf, pCtrlHdr); -+} -+ -+void VirtioGpuCmdHandler::cmdResourceFlush(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("ResourceFlush", pVirtqBuf, pCtrlHdr, sizeof(virtioGpu::ResourceFlush), -+ sizeof(virtioGpu::CtrlHdr))) { -+ return; -+ } -+ -+ virtioGpu::ResourceFlush request; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, request.payload(), sizeofPayload(request)); -+ LogRel7(("virtio-gpu cmd handler: Got RESOURCE_FLUSH command. (resource=%u, rect=w:%u,h:%u,x:%u,y:%u)\n", -+ request.uResourceId, request.r.width, request.r.height, request.r.x, request.r.y)); -+ -+ if (not checkResourceId("ResourceFlush", pVirtqBuf, pCtrlHdr, request.uResourceId)) { -+ return; -+ } -+ -+ auto scanouts {getScanoutsByResource(request.uResourceId)}; -+ if (scanouts.empty()) { -+ LogRel( -+ ("virtio-gpu cmd handler: ResourceFlush: No scanout is assigned to resource %u.\n", request.uResourceId)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_INVALID_RESOURCE_ID); -+ return; -+ } -+ -+ returnResponseOkEarly(pVirtqBuf, pCtrlHdr); -+ -+ for (auto& scanout : scanouts) { -+ if (scanout.get().hasDisplay()) { -+ scanout.get().flush(); -+ } -+ } -+ -+ returnResponseOkLate(pVirtqBuf, pCtrlHdr); -+} -+ -+void VirtioGpuCmdHandler::cmdTransferToHost2d(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("TransferToHost2D", pVirtqBuf, pCtrlHdr, sizeof(virtioGpu::TransferToHost2d), -+ sizeof(virtioGpu::CtrlHdr))) { -+ return; -+ } -+ -+ virtioGpu::TransferToHost2d request; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, request.payload(), sizeofPayload(request)); -+ LogRel7(("virtio-gpu cmd handler: Got TRANSFER_TO_HOST_2D command. (resource=%u, offset=%lu, " -+ "rect=w:%u,h:%u,x:%u,y:%u)\n", -+ request.uResourceId, request.uOffset, request.r.width, request.r.height, request.r.x, request.r.y)); -+ -+ if (not checkResourceId("TransferToHost2D", pVirtqBuf, pCtrlHdr, request.uResourceId)) { -+ return; -+ } -+ -+ auto scanouts {getScanoutsByResource(request.uResourceId)}; -+ if (scanouts.empty()) { -+ LogRel( -+ ("virtio-gpu cmd handler: ResourceFlush: No scanout is assigned to resource %u.\n", request.uResourceId)); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_INVALID_RESOURCE_ID); -+ return; -+ } -+ -+ returnResponseOkEarly(pVirtqBuf, pCtrlHdr); -+ -+ auto* resource {getResource(request.uResourceId)}; -+ auto vMapping {memoryAdapter_.mapGCPhys2HCVirt(resource->getBacking())}; -+ -+ PRTSGSEG paSegments {static_cast(RTMemAllocZ(sizeof(RTSGSEG) * vMapping.size()))}; -+ for (auto idx {0u}; idx < vMapping.size(); idx++) { -+ const auto& mapping {vMapping.at(idx)}; -+ paSegments[idx].pvSeg = mapping.uAddr_; -+ paSegments[idx].cbSeg = mapping.uLength_; -+ } -+ -+ PRTSGBUF pSegBuf {static_cast(RTMemAllocZ(sizeof(RTSGBUF)))}; -+ -+ for (auto& wrappedScanout : scanouts) { -+ auto& scanout {wrappedScanout.get()}; -+ if (not scanout.fActive) { -+ LogRel(("virtio-gpu cmd handler: TransferToHost2D: Prevented copying into disabled scanout %u.\n", -+ scanout.uScanoutId)); -+ continue; -+ } -+ -+ /* -+ * If the size is 64x64, then this is the resource of the mouse cursor. -+ * As we currently ignore the cursorq, we just do nothing in this case. -+ */ -+ if ((resource->width() > 64u and resource->height() > 64u) and scanout.hasDisplay() -+ and resource->getBacking().size() > 0 -+ /* -+ * TODO: at the moment we always assume that offset=0 and r.x=0 and r.y=0, -+ * i.e. the driver always sends a full frame, not just parts of a frame. -+ * This is currently only used by Linux and not by the customers driver, -+ * thus we ignore cases where this assumption isn't true. -+ */ -+ and request.r == virtioGpu::Rect {resource->width(), resource->height()}) { -+ RTSgBufInit(pSegBuf, paSegments, vMapping.size()); -+ auto [pFrameBuffer, cbFrameBuffer] {scanout.rDisplayManager.acquireBackingStore(scanout.uScanoutId)}; -+ if (pFrameBuffer != nullptr) { -+ RTSgBufCopyToBuf(pSegBuf, pFrameBuffer, cbFrameBuffer); -+ } -+ scanout.rDisplayManager.releaseBackingStore(); -+ } -+ } -+ -+ RTMemFree(pSegBuf); -+ RTMemFree(paSegments); -+ -+ memoryAdapter_.releaseMappings(vMapping); -+ -+ returnResponseOkLate(pVirtqBuf, pCtrlHdr); -+} -+ -+void VirtioGpuCmdHandler::cmdResourceAttachBacking(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("ResourceAttachBacking", pVirtqBuf, pCtrlHdr, sizeof(virtioGpu::ResourceAttachBacking), -+ sizeof(virtioGpu::CtrlHdr))) { -+ return; -+ } -+ -+ virtioGpu::ResourceAttachBacking request; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, request.payload(), sizeofPayload(request)); -+ LogRel7(("virtio-gpu cmd handler: Got RESOURCE_ATTACH_BACKING command. (resource=%u)\n", request.uResourceId)); -+ -+ if (pVirtqBuf->cbPhysSend < (request.uNrEntries * sizeof(virtioGpu::ResourceMemEntry))) { -+ LogRel(("virtio-gpu cmd handler: ResourceAttachBacking: request buffer too small for all memory entries.\n")); -+ returnResponseNoData(pVirtqBuf, pCtrlHdr, virtioGpu::CtrlType::Response::ERR_OUT_OF_MEMORY); -+ return; -+ } -+ -+ if (not checkResourceId("ResourceAttachBacking", pVirtqBuf, pCtrlHdr, request.uResourceId)) { -+ return; -+ } -+ -+ returnResponseOkEarly(pVirtqBuf, pCtrlHdr); -+ -+ auto* resource {getResource(request.uResourceId)}; -+ resource->reserveBacking(request.uNrEntries); -+ -+ const size_t cbEntries {sizeof(virtioGpu::ResourceMemEntry) * request.uNrEntries}; -+ virtioGpu::ResourceMemEntry* pEntries {static_cast(RTMemAlloc(cbEntries))}; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, pEntries, cbEntries); -+ -+ for (auto idx {0u}; idx < request.uNrEntries; idx++) { -+ resource->addBacking(pEntries[idx].uAddr, pEntries[idx].uLength); -+ } -+ -+ RTMemFree(pEntries); -+ -+ returnResponseOkLate(pVirtqBuf, pCtrlHdr); -+} -+ -+void VirtioGpuCmdHandler::cmdResourceDetachBacking(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr) -+{ -+ if (not checkCtrlqCmd("ResourceDetachBacking", pVirtqBuf, pCtrlHdr, sizeof(virtioGpu::ResourceDetachBacking), -+ sizeof(virtioGpu::CtrlHdr))) { -+ return; -+ } -+ -+ virtioGpu::ResourceDetachBacking request; -+ virtioAdapter_.virtqBufDrain(pVirtqBuf, request.payload(), sizeofPayload(request)); -+ LogRel7(("virtio-gpu cmd handler: Got RESOURCE_DETACH_BACKING command. (resource=%u)\n", request.uResourceId)); -+ -+ if (not checkResourceId("ResourceDetachBacking", pVirtqBuf, pCtrlHdr, request.uResourceId)) { -+ return; -+ } -+ -+ returnResponseOkEarly(pVirtqBuf, pCtrlHdr); -+ -+ auto* resource {getResource(request.uResourceId)}; -+ resource->clearBacking(); -+ -+ returnResponseOkLate(pVirtqBuf, pCtrlHdr); -+} -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpuCmdHandler.hpp b/src/VBox/Devices/Graphics/DevVirtioGpuCmdHandler.hpp -new file mode 100644 -index 0000000000..7cf4bf583d ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpuCmdHandler.hpp -@@ -0,0 +1,353 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#pragma once -+ -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "../VirtIO/VirtioCore.h" -+ -+#include "DevVirtioGpuResource.hpp" -+#include "DevVirtioGpuDefinitions.hpp" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+class VirtioGpuCmdHandler -+{ -+public: -+ /** -+ * A VirtioAdapter encapsulates functions to receive data from a virtq, -+ * put data into a virtq and signal to the guest that there is new data -+ * in a virtq. -+ */ -+ class VirtioAdapter -+ { -+ public: -+ /* Drains cb bytes from the pVirtqBuf into pv. */ -+ virtual void virtqBufDrain(PVIRTQBUF pVirtqBuf, void* pv, size_t cb) = 0; -+ -+ /* Puts pv into the used ring and thus sends data to the guest. */ -+ virtual void virtqBufPut(PVIRTQBUF pVirtqBuf, void* pv, size_t cb) = 0; -+ -+ /* Informs the guest driver about new data in the virtq. */ -+ virtual void virtqSyncRings(PVIRTQBUF pVirtqBuf) = 0; -+ }; -+ -+ /** -+ * The DisplayManager is the interface to the virtual displays provided by -+ * the hypervisor platform. -+ * It is responsible for updating the dimensions and the frames of the -+ * virtual displays according to the data in the scanouts. -+ */ -+ class DisplayManager -+ { -+ public: -+ /** -+ * The Backing store information -+ */ -+ using BackingStoreInfo = std::pair; -+ using Dimension = std::pair; -+ -+ /** -+ * Check whether the display index is managed by the Manager. -+ * -+ * \param displayIndex The display index. -+ * -+ * \return true if the display is managed, false otherwise -+ */ -+ virtual bool isManaged(uint32_t displayIndex) = 0; -+ -+ /** -+ * Obtain the dimensions of the given display. -+ * -+ * \param displayIndex The display index -+ * -+ * \return the dimension of the display or <0,0> if not managed -+ */ -+ virtual Dimension displayDimension(uint32_t displayIndex) = 0; -+ -+ /** -+ * Resize the given display to the given width and height. -+ * -+ * \param displayIndex The display index -+ * \param uWidth display width -+ * \param uHeigth display height -+ * \param i32OriginX The x position of the display to resize -+ * \param i32OriginY The y position of the display to resize -+ */ -+ virtual void resize(uint32_t displayIndex, uint32_t uWidth, uint32_t uHeight, -+ std::optional i32OriginX = std::nullopt, -+ std::optional i32OriginY = std::nullopt) = 0; -+ -+ /** -+ * Attaches the virtio-gpu to the given display. -+ * -+ * \param displayIndex The display index -+ */ -+ virtual int attachDisplay(uint32_t displayIndex) = 0; -+ -+ /** -+ * Detaches the virtio-gpu from the given display. -+ * -+ * \param displayIndex The display index -+ */ -+ virtual void detachDisplay(uint32_t displayIndex) = 0; -+ -+ /** -+ * Check attachment status of the given display. -+ * -+ * \param displayIndex The display index -+ * -+ * \return true if the display is attached, false otherwise. -+ */ -+ virtual bool isAttached(uint32_t displayIndex) = 0; -+ -+ /** -+ * Displays the framebuffer content on the display. -+ * -+ * \param displayIndex The display index -+ */ -+ virtual void display(uint32_t displayIndex) = 0; -+ -+ /** -+ * Obtain backing store information. -+ * -+ * \param displayIndex The display index -+ * -+ * \return Backing store information -+ */ -+ virtual BackingStoreInfo acquireBackingStore(uint32_t displayIndex) = 0; -+ -+ /** -+ * Releases the backing store, after an update of the frame buffer is done. -+ */ -+ virtual void releaseBackingStore() = 0; -+ }; -+ -+ /** -+ * A memoryAdapter is used to map guest physical addresses into the host's -+ * address space and to also unmap these mappings. -+ */ -+ class MemoryAdapter -+ { -+ protected: -+ struct VirtioGpuMapping -+ { -+ void* uAddr_; // The host virtual address -+ const uint32_t uLength_; // The size of the mapping -+ void* pv_; // A pointer to data the adapter may need to free this mapping later -+ -+ VirtioGpuMapping() = delete; -+ VirtioGpuMapping(void* uAddr, const uint32_t uLength, void* pv) : uAddr_(uAddr), uLength_(uLength), pv_(pv) -+ {} -+ }; -+ -+ using VecMemEntries = std::vector; -+ using VecMappings = std::vector; -+ -+ public: -+ /** -+ * Translates the guest physical addresses given in vBacking into host -+ * virtual addresses. -+ */ -+ virtual VecMappings mapGCPhys2HCVirt(const VecMemEntries& vBacking) = 0; -+ -+ /** -+ * Returns the mappings to the adapter so the adapter can do whatever -+ * is necessary. -+ */ -+ virtual void releaseMappings(const VecMappings& vMapping) = 0; -+ }; -+ -+private: -+ VirtioAdapter& virtioAdapter_; -+ DisplayManager& displayManager_; -+ MemoryAdapter& memoryAdapter_; -+ const uint32_t numScanouts_; -+ RTCLockMtx mutex_; -+ -+ struct Scanout -+ { -+ uint32_t uScanoutId {0}; -+ uint32_t uResourceId {0}; -+ uint32_t uCurrentWidth {0}; -+ uint32_t uCurrentHeight {0}; -+ uint32_t uResizedWidth {0u}; -+ uint32_t uResizedHeight {0u}; -+ bool fActive {false}; -+ bool fNeedsResize {true}; -+ bool fResizeRequested {false}; -+ -+ DisplayManager& rDisplayManager; -+ -+ Scanout() = delete; -+ Scanout(DisplayManager& dManager) : rDisplayManager(dManager) {} -+ -+ bool hasDisplay() const { return rDisplayManager.isManaged(uScanoutId); } -+ -+ bool isAttachedToDisplay() const { return hasDisplay() and rDisplayManager.isAttached(uScanoutId); } -+ -+ void attachDisplay() const -+ { -+ if (hasDisplay() and not isAttachedToDisplay()) { -+ rDisplayManager.attachDisplay(uScanoutId); -+ } -+ } -+ -+ void detachDisplay() const -+ { -+ if (isAttachedToDisplay()) { -+ rDisplayManager.detachDisplay(uScanoutId); -+ } -+ } -+ -+ DisplayManager::Dimension displayDimensions() const { return rDisplayManager.displayDimension(uScanoutId); } -+ -+ void resizeDisplay() { rDisplayManager.resize(uScanoutId, uCurrentWidth, uCurrentHeight); } -+ -+ void flush() { rDisplayManager.display(uScanoutId); } -+ }; -+ -+ using ScanoutRef = std::reference_wrapper; -+ using ScanoutCRef = std::reference_wrapper; -+ -+ std::vector> vResources_; -+ -+ /* Returns a pointer to the resource with the given ID, or a nullptr if there is no resource with the given ID. */ -+ inline VirtioGpuResource* getResource(uint32_t uResourceId); -+ -+ /* Creates a resource with the given ID. Returns false if the ID was already in use, true otherwise. */ -+ inline bool createResource(uint32_t uResourceId); -+ -+ /* Removes the resource with the given ID. */ -+ inline void removeResource(uint32_t uResourceId); -+ -+ std::vector activeScanouts; -+ -+ /* Returns true if the given scanout is in the range of existing scanouts (0 to NUM_MAX_SCANOUTS-1), false otherwise -+ */ -+ inline bool scanoutExists(uint32_t uScanout); -+ -+ /* Returns a reference to a scanout if the given scanout exists, an empty optional otherwise. */ -+ inline std::optional getScanout(uint32_t uScanout); -+ -+ /** -+ * Returns a vector filled with references to all scanouts with the given resourceId, or an empty optional if no -+ * scanout is assigned to the given resourceId. -+ */ -+ inline std::vector getScanoutsByResource(uint32_t uResourceId); -+ -+ /* Returns a dummy size to initialize the scanouts in case we are a secondary graphics controller. */ -+ inline std::tuple getDummySize() -+ { -+ return std::make_tuple(virtioGpu::INITIAL_WIDTH, virtioGpu::INITIAL_HEIGHT); -+ } -+ -+public: -+ VirtioGpuCmdHandler() = delete; -+ VirtioGpuCmdHandler(VirtioAdapter& vAdapter, DisplayManager& dManager, MemoryAdapter& mAdapter, -+ uint32_t numScanouts, bool attachDisplayLater); -+ -+ ~VirtioGpuCmdHandler() = default; -+ -+ /* Destroys all existing resources. */ -+ void clearResources() { vResources_.clear(); } -+ -+ /** -+ * Returns a const reference to the scanout with the given ID if it exists. -+ * That way another class working with the cmdHandler can't modify the scanouts. -+ */ -+ std::optional getCScanout(uint32_t uScanout); -+ -+ /** -+ * Requests resizing of the monitor. Sets the uResizedWidth and uResizedHeight variables of the associated scanout -+ * and sets scanout.fResizeRequested to true. The next getDisplayInfo or getEdid will then use the new resolution. -+ */ -+ void requestResize(uint32_t uScanout, bool enabled, uint32_t uWidth, uint32_t uHeight); -+ -+ /** Updates the current width and height of the given scanout and resizes display if necessary. */ -+ inline void resizeScanout(uint32_t uScanout, uint32_t uWidth, uint32_t uHeight); -+ -+ /* Handles the given virtq buffer and returns a response to the driver. */ -+ void handleBuffer(PVIRTQBUF pVirtqBuf); -+ -+ /* Does some basic sanity checking and returns an appropriate error to the guest if something is broken. */ -+ inline bool checkCtrlqCmd(const char* cmdName, PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr, size_t cbSend, -+ size_t cbReturn); -+ -+ /* Checks wether the given scanout id is valid and returns a ERR_INVALID_SCANOUT_ID header to the guest if it isn't. -+ */ -+ inline bool checkScanoutId(const char* cmdName, PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr, -+ uint32_t uScanoutId); -+ -+ /* Checks wether the given resource id is valid and returns a ERR_INVALID_RESOURCE_ID header to the guest if it -+ * isn't. */ -+ inline bool checkResourceId(const char* cmdName, PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr, -+ uint32_t uResourceId); -+ -+ /* Returns a CtrlType::Response::OK_NODATA if the FENCE-flag is not set */ -+ inline void returnResponseOkEarly(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Returns a CtrlType::Response::OK_NODATA if the FENCE-flag is set */ -+ inline void returnResponseOkLate(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Returns a header with the given resonseType to the driver. */ -+ inline void returnResponseNoData(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr, -+ virtioGpu::CtrlType::Response responseType); -+ -+ /* Returns a response buffer to the driver. */ -+ inline void returnResponseBuf(PVIRTQBUF pVirtqBuf, void* pv, size_t cb); -+ -+ /* Returns the current output configureation to the driver. */ -+ void cmdGetDisplayInfo(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Return the EDID data for a given scanout to the driver. */ -+ void cmdGetEdid(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Creates a 2D resource on the host. */ -+ void cmdResourceCreate2d(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Destroys a 2D resource. */ -+ void cmdResourceUnref(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Sets the scanout parameters for a given output. */ -+ void cmdSetScanout(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Flushes a resource to the screen. */ -+ void cmdResourceFlush(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Transfers guest memory to host memory. */ -+ void cmdTransferToHost2d(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Attaches backing pages to a resource. */ -+ void cmdResourceAttachBacking(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+ -+ /* Removes backing pages from a resource. */ -+ void cmdResourceDetachBacking(PVIRTQBUF pVirtqBuf, virtioGpu::CtrlHdr* pCtrlHdr); -+}; -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpuDefinitions.hpp b/src/VBox/Devices/Graphics/DevVirtioGpuDefinitions.hpp -new file mode 100644 -index 0000000000..fbb2fa78e8 ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpuDefinitions.hpp -@@ -0,0 +1,360 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#pragma once -+ -+#include -+ -+namespace virtioGpu -+{ -+ -+constexpr uint32_t INITIAL_WIDTH {1920u}; -+constexpr uint32_t INITIAL_HEIGHT {1080u}; -+ -+/** -+ * Virtio 1.2 - 4.1.2 PCI Device Discovery -+ * The PCI Device ID is calculated by adding 0x1040 to the Virtio Device ID. -+ */ -+enum : uint16_t -+{ -+ DEVICE_ID = 16, -+ PCI_DEVICE_ID = 0x1040 + DEVICE_ID, -+ PCI_CLASS_BASE = 0x03, ///< GPU -+ PCI_CLASS_SUB = 0x00, ///< VGA compatible -+ PCI_CLASS_PROG = 0x00, ///< Unspecified -+ PCI_INTERRUPT_LINE = 0x00, -+ PCI_INTERRUPT_PIN = 0x01, -+}; -+ -+/** -+ * Virtio 1.2 - 5.7.1 GPU Device Feature bits -+ */ -+enum Features : uint16_t -+{ -+ VIRGIL = 1u << 0, ///< virgl 3D mode is supported -+ EDID = 1u << 1, ///< EDID (Extended Display Identification Data) is supported -+ RESOURCE_UUID = 1u << 2, ///< assigning resources UUIDs for export to other virtio devices is supported -+ RESOURCE_BLOB = 1u << 3, ///< creating and using size-based blob resources is supported -+ CONTEXT_INIT = 1u << 4, ///< multiple context types and synchronization timelines supported -+}; -+ -+/** -+ * Virtio 1.2 - 5.7.2 GPU Device Virtqueues -+ */ -+constexpr unsigned NUM_VIRTQUEUES {2u}; -+ -+enum VirtqIdx : uint16_t -+{ -+ CONTROLQ = 0, ///< The index of the controlqueue -+ CURSORQ = 1, ///< The index of the cursorqueue -+}; -+ -+/** -+ * Virtio 1.2 - 5.7.4 GPU Device configuration layout -+ * Virtio GPU device-specific configuration -+ */ -+struct Config -+{ -+ uint32_t uEventsRead {0u}; ///< Signals pending events to the driver -+ uint32_t uEventsClear {0u}; ///< Clears pending events in the device (write-to-clear) -+ uint32_t uNumScanouts {0u}; ///< Maximum number of scanouts supported (between 1 and 16 inclusive) -+ uint32_t uNumCapsets {0u}; ///< Maximum number of capability sets supported -+}; -+ -+static constexpr uint32_t EVENT_DISPLAY { -+ 1u << 0}; ///< display configuration has changed and should be fetched by the driver -+ -+/** -+ * Virtio 1.2 - 5.7.6.7 GPU Device Device Operation: Request header -+ */ -+struct CtrlType -+{ -+ enum Cmd : uint32_t -+ { -+ /* 2d commands */ -+ GET_DISPLAY_INFO = 0x0100, -+ RESOURCE_CREATE_2D, -+ RESOURCE_UNREF, -+ SET_SCANOUT, -+ RESOURCE_FLUSH, -+ TRANSFER_TO_HOST_2D, -+ RESOURCE_ATTACH_BACKING, -+ RESOURCE_DETACH_BACKING, -+ GET_CAPSET_INFO, -+ GET_CAPSET, -+ GET_EDID, -+ RESOURCE_ASSIGN_UUID, -+ RESOURCE_ASSIGN_BLOB, -+ SET_SCANOUT_BLOB, -+ -+ /* 3d commands */ -+ CTX_CREATE = 0x0200, -+ CTX_DESTROY, -+ CTX_ATTACH_RESOURCE, -+ CTX_DETACH_RESOURCE, -+ RESOURCE_CREATE_3D, -+ TRANSFER_TO_HOST_3D, -+ TRANSFER_FROM_HOST_3D, -+ SUBMIT_3D, -+ RESOURCE_MAP_BLOB, -+ RESOURCE_UNMAP_BLOB, -+ -+ /* cursor commands */ -+ UPDATE_CURSOR = 0x0300, -+ MOVE_CURSOR, -+ }; -+ -+ enum Response : uint32_t -+ { -+ /* success responses */ -+ OK_NODATA = 0x1100, -+ OK_DISPLAY_INFO, -+ OK_CAPSET_INFO, -+ OK_CAPSET, -+ OK_EDID, -+ OK_RESOURCE_UUID, -+ OK_MAP_INFO, -+ -+ /* error responses */ -+ ERR_UNSPEC = 0x1200, -+ ERR_OUT_OF_MEMORY, -+ ERR_INVALID_SCANOUT_ID, -+ ERR_INVALID_RESOURCE_ID, -+ ERR_INVALID_CONTEXT_ID, -+ ERR_INVALID_PARAMETER, -+ }; -+}; -+ -+/** -+ * Virtio 1.2 - 5.7.6.8 GPU Device Operation: controlq -+ * VIRTIO_GPU_CMD_RESOURCE_CREATE_2D possible formats -+ */ -+struct __attribute__((packed)) CtrlHdr -+{ -+ uint32_t uType {0}; ///< type specifies the type of driver request or device response -+ uint32_t uFlags {0}; ///< flags request/response flags -+ uint64_t uFenceId {0}; ///< fence_id only important if FLAG_FENCE bit is set in flags -+ uint32_t uCtxId {0}; ///< ctx_id rendering context (3D mode only) -+ uint8_t uRingIdx {0}; ///< ring_idx -+ uint8_t uPadding[3]; -+ -+ CtrlHdr() = default; -+ CtrlHdr(uint32_t uCmd) : uType(uCmd) {} -+ CtrlHdr(CtrlType::Cmd uCmd) : uType(uCmd) {} -+ -+ enum class Flags : uint32_t -+ { -+ FENCE = 1u << 0, -+ INFO_RING_IDX = 1u << 1, -+ }; -+ -+ /* Checks whether the given bit is set in uFlags */ -+ inline bool has_flag(Flags flag) const { return (uFlags & static_cast(flag)) != 0; }; -+ -+ /* Sets the given bit if fSet is true, otherwise clears it. */ -+ inline void set_flag(Flags flag, bool fSet) -+ { -+ const uint32_t mask {static_cast(flag)}; -+ uFlags &= ~mask | fSet ? mask : 0u; -+ }; -+ -+ /* Transfers the fence-flag and uFenceId from other to this. */ -+ inline void transfer_fence(const CtrlHdr* other) -+ { -+ if (other->has_flag(Flags::FENCE)) { -+ set_flag(Flags::FENCE, true); -+ uFenceId = other->uFenceId; -+ } -+ } -+}; -+ -+/* -+ * controlq command structure definitions -+ * See Virtio 1.2 - 5.7.6.8 -+ */ -+static void* removeHeader(void* pThis) -+{ -+ return reinterpret_cast(reinterpret_cast(pThis) + sizeof(CtrlHdr)); -+} -+ -+struct __attribute__((packed)) Rect -+{ -+ uint32_t x {0}; -+ uint32_t y {0}; -+ uint32_t width {0}; -+ uint32_t height {0}; -+ -+ Rect() = default; -+ Rect(uint32_t w, uint32_t h) : width(w), height(h) {} -+ -+ friend bool operator==(const Rect& lhs, const Rect& rhs) -+ { -+ return lhs.x == rhs.x and lhs.y == rhs.y and lhs.width == rhs.width and lhs.height == rhs.height; -+ } -+ -+ friend bool operator!=(const Rect& lhs, const Rect& rhs) { return not(lhs == rhs); } -+}; -+ -+struct __attribute__((packed)) DisplayOne -+{ -+ Rect r; -+ uint32_t enabled {0}; -+ uint32_t flags {0}; -+}; -+ -+struct __attribute__((packed)) ResponseDisplayInfo -+{ -+private: -+ static constexpr uint32_t NUM_MAX_SCANOUTS {16u}; -+ -+public: -+ CtrlHdr hdr {CtrlType::Response::OK_DISPLAY_INFO}; -+ DisplayOne pmodes[NUM_MAX_SCANOUTS]; -+ -+ static size_t size(uint32_t num_scanouts) { return sizeof(CtrlHdr) + num_scanouts * sizeof(DisplayOne); } -+}; -+ -+struct __attribute__((packed)) GetEdid -+{ -+ CtrlHdr hdr {CtrlType::Cmd::GET_EDID}; -+ uint32_t uScanout {0}; -+ uint32_t uPadding; -+ -+ void* payload() { return removeHeader(this); } -+}; -+ -+struct __attribute__((packed)) ResponseEdid -+{ -+ CtrlHdr hdr {CtrlType::Response::OK_EDID}; -+ uint32_t uSize {0}; -+ uint32_t uPadding; -+ uint8_t aEdid[1024] {0}; -+}; -+ -+enum Format : uint32_t -+{ -+ B8G8R8A8_UNORM = 1, -+ B8G8R8X8_UNORM = 2, -+ A8R8G8B8_UNORM = 3, -+ X8R8G8B8_UNORM = 4, -+ -+ R8G8B8A8_UNORM = 67, -+ X8B8G8R8_UNORM = 68, -+ -+ A8B8G8R8_UNORM = 121, -+ R8G8B8X8_UNORM = 134, -+}; -+ -+struct __attribute__((packed)) ResourceCreate2d -+{ -+ CtrlHdr hdr {CtrlType::Cmd::RESOURCE_CREATE_2D}; -+ uint32_t uResourceId {0}; -+ uint32_t uFormat {0}; -+ uint32_t uWidth {0}; -+ uint32_t uHeight {0}; -+ -+ ResourceCreate2d() = default; -+ ResourceCreate2d(uint32_t id) : uResourceId(id) {} -+ ResourceCreate2d(uint32_t id, uint32_t w, uint32_t h) : uResourceId(id), uWidth(w), uHeight(h) {} -+ -+ void* payload() { return removeHeader(this); } -+}; -+ -+struct __attribute__((packed)) ResourceUnref -+{ -+ CtrlHdr hdr {CtrlType::Cmd::RESOURCE_UNREF}; -+ uint32_t uResourceId {0}; -+ uint32_t uPadding; -+ -+ ResourceUnref() = default; -+ ResourceUnref(uint32_t id) : uResourceId(id) {} -+ -+ void* payload() { return removeHeader(this); } -+}; -+ -+struct __attribute__((packed)) SetScanout -+{ -+ CtrlHdr hdr {CtrlType::Cmd::SET_SCANOUT}; -+ Rect r; -+ uint32_t uScanoutId {0}; -+ uint32_t uResourceId {0}; -+ -+ SetScanout() = default; -+ SetScanout(uint32_t scanoutId, uint32_t resId) : uScanoutId(scanoutId), uResourceId(resId) {} -+ SetScanout(uint32_t scanoutId, uint32_t resId, uint32_t w, uint32_t h) -+ : r(w, h), uScanoutId(scanoutId), uResourceId(resId) -+ {} -+ -+ void* payload() { return removeHeader(this); } -+}; -+ -+struct __attribute__((packed)) ResourceFlush -+{ -+ CtrlHdr hdr {CtrlType::Cmd::RESOURCE_FLUSH}; -+ Rect r; -+ uint32_t uResourceId {0}; -+ uint32_t uPadding; -+ -+ void* payload() { return removeHeader(this); } -+}; -+ -+struct __attribute__((packed)) TransferToHost2d -+{ -+ CtrlHdr hdr {CtrlType::Cmd::TRANSFER_TO_HOST_2D}; -+ Rect r; -+ uint64_t uOffset {0}; -+ uint32_t uResourceId {0}; -+ uint32_t uPadding; -+ -+ TransferToHost2d() = default; -+ TransferToHost2d(uint32_t resId) : uResourceId(resId) {} -+ TransferToHost2d(uint32_t resId, uint32_t w, uint32_t h) : r(w, h), uResourceId(resId) {} -+ -+ void* payload() { return removeHeader(this); } -+}; -+ -+struct __attribute__((packed)) ResourceAttachBacking -+{ -+ CtrlHdr hdr {CtrlType::Cmd::RESOURCE_ATTACH_BACKING}; -+ uint32_t uResourceId {0}; -+ uint32_t uNrEntries {0}; -+ -+ void* payload() { return removeHeader(this); } -+}; -+ -+struct __attribute__((packed)) ResourceMemEntry -+{ -+ uint64_t uAddr {0}; -+ uint32_t uLength {0}; -+ uint32_t uPadding; -+}; -+ -+struct __attribute__((packed)) ResourceDetachBacking -+{ -+ CtrlHdr hdr {CtrlType::Cmd::RESOURCE_DETACH_BACKING}; -+ uint32_t uResourceId {0}; -+ uint32_t uPadding; -+ -+ ResourceDetachBacking() = default; -+ ResourceDetachBacking(uint32_t id) : uResourceId(id) {} -+ -+ void* payload() { return removeHeader(this); } -+}; -+ -+} // namespace virtioGpu -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpuDisplayManager.cpp b/src/VBox/Devices/Graphics/DevVirtioGpuDisplayManager.cpp -new file mode 100644 -index 0000000000..7a04ca9d5c ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpuDisplayManager.cpp -@@ -0,0 +1,413 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_GPU -+#include "DevVirtioGpuDisplayManager.hpp" -+ -+#include "DevVirtioGpuResource.hpp" -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+ -+VirtioGpuDisplayManager::VirtioGpuDisplayManager(PPDMDEVINS pDevIns_, unsigned iLUN_, PDMIBASE& iBase_, -+ uint32_t u32VRamSize, uint32_t u32MonitorCount_) -+ : pDevIns(pDevIns_), iLUN(iLUN_), iBase(iBase_), u32MonitorCount(u32MonitorCount_) -+{ -+ vram.resize(u32VRamSize); -+ -+ uint32_t viewIndex {0ul}; -+ -+ auto generateDisplays = [&viewIndex, &u32VRamSize]() { -+ Display display; -+ display.view.u32ViewIndex = viewIndex; -+ display.view.u32ViewSize = u32VRamSize; -+ /* -+ * We allow the free use of the assigned VRAM, so it is irrelevant if -+ * there are multiple monitors of a mid size resolution or one single monitor with a huge resolution -+ */ -+ display.view.u32MaxScreenSize = u32VRamSize; -+ -+ display.screen.u32ViewIndex = viewIndex; -+ display.screen.u16BitsPerPixel = VirtioGpuResource::BYTES_PER_PIXEL * __CHAR_BIT__; -+ display.screen.u32Width = virtioGpu::INITIAL_WIDTH; -+ display.screen.u32Height = virtioGpu::INITIAL_HEIGHT; -+ -+ display.screen.i32OriginX = viewIndex * virtioGpu::INITIAL_WIDTH; -+ display.screen.i32OriginY = 0; -+ -+ display.screen.u16Flags = VBVA_SCREEN_F_DISABLED; -+ -+ viewIndex++; -+ -+ return display; -+ }; -+ -+ std::generate_n(std::back_insert_iterator>(displays), u32MonitorCount, generateDisplays); -+ std::memset(vram.data(), 0, vram.size()); -+} -+ -+VirtioGpuDisplayManager::~VirtioGpuDisplayManager() -+{} -+ -+void VirtioGpuDisplayManager::reset() -+{ -+ DriverGuard _ {driverMtx}; -+ -+ if (not pDrv) { -+ return; -+ } -+ -+ if (pDrv->pfnReset != nullptr) { -+ pDrv->pfnReset(pDrv); -+ } -+} -+ -+bool VirtioGpuDisplayManager::isManaged(uint32_t displayIndex) -+{ -+ return displayIndex < u32MonitorCount; -+} -+ -+VirtioGpuDisplayManager::Dimension VirtioGpuDisplayManager::displayDimension(uint32_t displayIndex) -+{ -+ if (not isManaged(displayIndex)) { -+ return {0, 0}; -+ } -+ -+ DriverGuard _ {driverMtx}; -+ -+ auto& display {displays.at(displayIndex)}; -+ -+ return {display.screen.u32Width, display.screen.u32Height}; -+} -+ -+void VirtioGpuDisplayManager::resize(uint32_t displayIndex, uint32_t uWidth, uint32_t uHeight, -+ std::optional i32OriginX, std::optional i32OriginY) -+{ -+ if (not isAttached(displayIndex)) { -+ return; -+ } -+ -+ DriverGuard _ {driverMtx}; -+ -+ auto& screen {displays.at(displayIndex).screen}; -+ -+ const uint32_t bytesPerPixel {screen.u16BitsPerPixel / static_cast(__CHAR_BIT__)}; -+ -+ int32_t newOriginX {i32OriginX.value_or(screen.i32OriginX)}; -+ int32_t newOriginY {i32OriginY.value_or(screen.i32OriginY)}; -+ -+ screen.u32LineSize = uWidth * bytesPerPixel; -+ screen.u32Width = uWidth; -+ screen.u32Height = uHeight; -+ screen.i32OriginX = newOriginX; -+ screen.i32OriginY = newOriginY; -+ -+ /* -+ * The Framebuffers of all displays are handled in a consecutive buffer of memory by VBox, which is called the VRAM -+ * During a Monitor resize, the portion of memory used for the desired monitor changes. -+ * Thus we need to adjust the start offset of the next monitor to avoid wrong graphics output. -+ */ -+ for (uint32_t i {displayIndex + 1}; i < displays.size(); ++i) { -+ auto& currentScreen {displays.at(i).screen}; -+ auto& previousScreen {displays.at(i - 1).screen}; -+ -+ auto calculateScreenSize = [](auto& screen_) -> uint32_t { -+ return screen_.u32Width * screen_.u32Height -+ * (screen_.u16BitsPerPixel / static_cast(__CHAR_BIT__)); -+ }; -+ -+ uint32_t previousScreenSize {calculateScreenSize(previousScreen)}; -+ uint32_t newStartOffset {previousScreen.u32StartOffset + previousScreenSize}; -+ -+ currentScreen.u32StartOffset = newStartOffset; -+ -+ int64_t xOffset {previousScreen.i32OriginX + previousScreen.u32Width}; -+ -+ currentScreen.i32OriginX = xOffset; -+ resizeVBVA(i, true); -+ -+ uint32_t screenSize {calculateScreenSize(currentScreen)}; -+ AssertLogRelMsg((currentScreen.u32StartOffset + screenSize) < vram.size(), -+ ("VirtioGpuDisplayManager: The framebuffer for the displays starting with index %u does not " -+ "fit into VRAM, monitorCount %u \n", -+ i, displays.size())); -+ } -+ -+ resizeVBVA(displayIndex, true); -+} -+ -+int VirtioGpuDisplayManager::attachDisplay(uint32_t displayIndex) -+{ -+ int rc {VINF_SUCCESS}; -+ if (isAttached(displayIndex)) { -+ return VINF_SUCCESS; -+ } -+ -+ if (not isManaged(displayIndex)) { -+ return VERR_NOT_AVAILABLE; -+ } -+ -+ { -+ DriverGuard _ {driverMtx}; -+ -+ auto& display {displays.at(displayIndex)}; -+ -+ LogRel6(("VirtioGpuDisplayManager: attaching monitor %u .\n", display.view.u32ViewIndex)); -+ -+ display.screen.u16Flags = VBVA_SCREEN_F_ACTIVE; -+ } -+ -+ if (not allDisplaysDetached() and not ownDisplay) { -+ PVM pVM {PDMDevHlpGetVM(pDevIns)}; -+ -+ rc = PDMR3DriverDetach(pVM->pUVM, "vga", 0, 0, NULL, 0, PDM_TACH_FLAGS_NOT_HOT_PLUG); -+ AssertLogRel(RT_SUCCESS(rc)); -+ -+ rc = PDMR3DriverAttach(pVM->pUVM, "vga", 0, 0, PDM_ATTACH_DUMMY_DRIVER, NULL); -+ -+ if (not pDrv) { -+ rc = takeoverDriver(); -+ AssertLogRel(RT_SUCCESS(rc)); -+ } -+ ownDisplay = true; -+ return rc; -+ } -+ -+ rc = enableVBVA(displayIndex); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ rc = resizeVBVA(displayIndex, false); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ return rc; -+} -+ -+void VirtioGpuDisplayManager::detachDisplay(uint32_t displayIndex) -+{ -+ if (not isAttached(displayIndex)) { -+ LogRel(("Display %d not attached. Not going to do anything\n", displayIndex)); -+ return; -+ } -+ -+ DriverGuard _ {driverMtx}; -+ -+ if (not isManaged(displayIndex)) { -+ LogRel(("Display %d not managed. Not going to do anything\n", displayIndex)); -+ return; -+ } -+ -+ auto& display {displays.at(displayIndex)}; -+ -+ LogRel6(("VirtioGpuDisplayManager: detaching monitor %u.\n", displayIndex)); -+ -+ display.screen.u16Flags = VBVA_SCREEN_F_DISABLED; -+ -+ /** -+ * On display termination, the pDrv is handed back to VBox already. Thus -+ * the error code VERR_NOT_AVAILABLE is reported here. -+ */ -+ resizeVBVA(displayIndex, false); -+ -+ disableVBVA(displayIndex); -+} -+ -+bool VirtioGpuDisplayManager::isAttached(uint32_t displayIndex) -+{ -+ DriverGuard _ {driverMtx}; -+ -+ if (not isManaged(displayIndex)) { -+ return false; -+ } -+ -+ auto& display {displays.at(displayIndex)}; -+ -+ return display.screen.u16Flags & VBVA_SCREEN_F_ACTIVE; -+} -+ -+void VirtioGpuDisplayManager::display(uint32_t displayIndex) -+{ -+ if (not isAttached(displayIndex)) { -+ return; -+ } -+ -+ DriverGuard _ {driverMtx}; -+ if (pDrv != nullptr) { -+ auto& screen {displays.at(displayIndex).screen}; -+ VBVACMDHDR cmd; -+ -+ cmd.x = screen.i32OriginX; -+ cmd.y = screen.i32OriginY; -+ cmd.w = screen.u32Width; -+ cmd.h = screen.u32Height; -+ -+ pDrv->pfnVBVAUpdateBegin(pDrv, screen.u32ViewIndex); -+ pDrv->pfnVBVAUpdateProcess(pDrv, screen.u32ViewIndex, &cmd, sizeof(cmd)); -+ pDrv->pfnVBVAUpdateEnd(pDrv, screen.u32ViewIndex, screen.i32OriginX, screen.i32OriginY, screen.u32Width, -+ screen.u32Height); -+ } -+} -+ -+VirtioGpuDisplayManager::BackingStoreInfo VirtioGpuDisplayManager::acquireBackingStore(uint32_t displayIndex) -+{ -+ driverMtx.lock(); -+ -+ if (not isManaged(displayIndex) or pDrv == nullptr) { -+ return {nullptr, 0}; -+ } -+ -+ auto& screen {displays.at(displayIndex).screen}; -+ -+ uint8_t* pFramebuffer {vram.data() + screen.u32StartOffset}; -+ size_t cbFramebuffer {screen.u32Width * screen.u32Height -+ * (screen.u16BitsPerPixel / static_cast(__CHAR_BIT__))}; -+ -+ return {pFramebuffer, cbFramebuffer}; -+} -+ -+void VirtioGpuDisplayManager::releaseBackingStore() -+{ -+ driverMtx.unlock(); -+} -+ -+bool VirtioGpuDisplayManager::allDisplaysDetached() -+{ -+ auto attachementFn = [](Display& display) { return display.screen.u16Flags & VBVA_SCREEN_F_ACTIVE; }; -+ -+ return std::find_if(displays.begin(), displays.end(), attachementFn) == displays.end(); -+} -+ -+int VirtioGpuDisplayManager::takeoverDriver() -+{ -+ int rc {VINF_SUCCESS}; -+ -+ if (pDrvBase == nullptr) { -+ rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &iBase, &pDrvBase, "Display Port"); -+ } -+ -+ if (rc == VERR_PDM_NO_ATTACHED_DRIVER) { -+ AssertLogRelMsgFailed(("VirtioGpuDisplayManager: %s/%d: warning: no driver attached to LUN #0!\n", -+ pDevIns->pReg->szName, pDevIns->iInstance)); -+ return VINF_SUCCESS; -+ } else if (not RT_SUCCESS(rc)) { -+ AssertLogRelMsgFailed(("VirtioGpuDisplayManager: failed to attach LUN #0! rc=%Rrc\n", rc)); -+ return rc; -+ } -+ -+ /* rc == VINF_SUCCESS, i.e. pDrvBase is attached to iLUN */ -+ -+ pDrv = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIDISPLAYCONNECTOR); -+ if (pDrv != nullptr) { -+ if (pDrv->pfnRefresh == nullptr or pDrv->pfnResize == nullptr or pDrv->pfnUpdateRect == nullptr) { -+ Assert(pDrv->pfnRefresh != nullptr); -+ Assert(pDrv->pfnResize != nullptr); -+ Assert(pDrv->pfnUpdateRect != nullptr); -+ pDrv = nullptr; -+ pDrvBase = nullptr; -+ rc = VERR_INTERNAL_ERROR; -+ } -+ } else { -+ AssertLogRelMsgFailed( -+ ("VirtioGpuDisplayManager: LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc)); -+ pDrvBase = nullptr; -+ rc = VERR_PDM_MISSING_INTERFACE; -+ } -+ -+ LogRel2(("VirtioGpuDisplayDriver: Display Port Driver attached\n")); -+ -+ /* -+ * We deactivate the rendering of the mouse cursor by VBox, as the intel driver of the Windows -+ * VM renders the mouse cursor for the VM already. -+ */ -+ pDrv->pfnVBVAMousePointerShape(pDrv, false, false, 0, 0, 0, 0, nullptr); -+ return rc; -+} -+ -+void VirtioGpuDisplayManager::handoverDriver() -+{ -+ PVM pVM {PDMDevHlpGetVM(pDevIns)}; -+ PDMR3DriverDetach(pVM->pUVM, "virtio-gpu", 0, 0, NULL, 0, 0); -+ PDMR3DriverAttach(pVM->pUVM, "vga", 0, 0, PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL); -+ -+ pDrv = nullptr; -+ pDrvBase = nullptr; -+ ownDisplay = false; -+ -+ LogRel2(("VirtioGpuDisplayDriver: Display Port Driver detached\n")); -+} -+ -+void VirtioGpuDisplayManager::detachAllDisplays() -+{ -+ for (auto i {0ul}; i < displays.size(); ++i) { -+ detachDisplay(i); -+ } -+} -+ -+int VirtioGpuDisplayManager::resizeVBVA(uint32_t displayIndex, bool fResetInputMapping) -+{ -+ AssertLogRelMsgReturn(isManaged(displayIndex), -+ ("VirtioGpuDisplayManager: UpdateVBVA: The Display %u is not managed! \n", displayIndex), -+ VERR_INVALID_PARAMETER); -+ -+ int rc {VERR_NOT_AVAILABLE}; -+ -+ if (pDrv != nullptr and pDrv->pfnVBVAResize != nullptr) { -+ AssertRelease(isManaged(displayIndex)); -+ -+ auto& display {displays.at(displayIndex)}; -+ -+ return pDrv->pfnVBVAResize(pDrv, &display.view, &display.screen, vram.data(), fResetInputMapping); -+ } -+ -+ LogRel6(("VirtioGpuDisplayManager: tried to update VBVA for display %u. Return Code: %Rrc.\n", displayIndex, rc)); -+ -+ return rc; -+} -+ -+int VirtioGpuDisplayManager::enableVBVA(uint32_t displayIndex) -+{ -+ AssertLogRelMsgReturn(isManaged(displayIndex), -+ ("VirtioGpuDisplayManager: EnableVBVA: The display %u is not managed! \n", displayIndex), -+ VERR_INVALID_PARAMETER); -+ -+ int rc {VERR_NOT_AVAILABLE}; -+ -+ if (pDrv != nullptr and pDrv->pfnVBVAEnable != nullptr) { -+ rc = pDrv->pfnVBVAEnable(pDrv, displayIndex, 0); -+ } -+ -+ LogRel6(("VirtioGpuDisplayManager: tried to enable VBVA for display %u. Return Code: %Rrc.\n", displayIndex, rc)); -+ -+ return rc; -+} -+ -+void VirtioGpuDisplayManager::disableVBVA(uint32_t displayIndex) -+{ -+ AssertReleaseMsg(isManaged(displayIndex), -+ ("VirtioGpuDisplayManager: disableVBVA: The display %u is not managed! \n", displayIndex)); -+ -+ if (pDrv != nullptr and pDrv->pfnVBVADisable != nullptr) { -+ pDrv->pfnVBVADisable(pDrv, displayIndex); -+ LogRel6(("VirtioGpuDisplayManager: disabled VBVA for display %u.\n", displayIndex)); -+ } -+} -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpuDisplayManager.hpp b/src/VBox/Devices/Graphics/DevVirtioGpuDisplayManager.hpp -new file mode 100644 -index 0000000000..838a23602d ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpuDisplayManager.hpp -@@ -0,0 +1,213 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#pragma once -+ -+#include -+#include -+#include -+ -+#include "DevVirtioGpuCmdHandler.hpp" -+#include -+ -+#include -+ -+/** -+ * The Virtio Display Manager implementation. -+ * -+ */ -+class VirtioGpuDisplayManager final : public VirtioGpuCmdHandler::DisplayManager -+{ -+public: -+ VirtioGpuDisplayManager() = delete; -+ -+ /** -+ * The Virtio GPU Display Manager. -+ * -+ * \param pDevINs_ The VBox PDM Device Instance Data -+ * \param iLUN_ The device LUN of the GPU -+ * \param iBase_ The Virtio GPU Interface Base -+ * \param u32VRamSize The assigned VRAM -+ * \param u32MonitorCount The maximum active monitor count -+ */ -+ VirtioGpuDisplayManager(PPDMDEVINS pDevIns_, unsigned iLUN_, PDMIBASE& iBase_, uint32_t u32VRamSize, -+ uint32_t u32MonitorCount); -+ -+ ~VirtioGpuDisplayManager(); -+ -+ /** -+ * Reset the Display infrastructure -+ */ -+ void reset(); -+ -+ /** -+ * Check whether the display index is managed by the Manager. -+ * -+ * \param displayIndex The display index. -+ * -+ * \return true if the display is managed, false otherwise -+ */ -+ virtual bool isManaged(uint32_t displayIndex) final; -+ -+ /** -+ * Obtain the dimensions of the given display. -+ * -+ * \param displayIndex The display index -+ * -+ * \return the dimension of the display or <0,0> if not managed -+ */ -+ virtual Dimension displayDimension(uint32_t displayIndex) final; -+ -+ /** -+ * Resize the given display to the given width and height. -+ * -+ * \param displayIndex The display index -+ * \param uWidth display width -+ * \param uHeigth display height -+ * \param u32OriginX (Optional) The display x position -+ * \param u32OriginY (Optional) The display y position -+ */ -+ virtual void resize(uint32_t displayIndex, uint32_t uWidth, uint32_t uHeight, std::optional i32OriginX, -+ std::optional i32OriginY) final; -+ -+ /** -+ * Attaches the virtio-gpu to the given display. -+ * -+ * \param displayIndex The display index -+ */ -+ virtual int attachDisplay(uint32_t displayIndex) final; -+ -+ /** -+ * Detaches the virtio-gpu from the given display. -+ * -+ * \param displayIndex The display index -+ */ -+ virtual void detachDisplay(uint32_t displayIndex) final; -+ -+ /** -+ * Check attachment status of the given display. -+ * -+ * \param displayIndex The display index -+ * -+ * \return true if the display is attached, false otherwise. -+ */ -+ virtual bool isAttached(uint32_t displayIndex) final; -+ /** -+ * Displays the framebuffer content on the display. -+ * -+ * \param displayIndex The display index -+ */ -+ virtual void display(uint32_t displayIndex) final; -+ -+ /** -+ * Obtain backing store. -+ * -+ * The function activates the pDrv lock, to ensure consistency. -+ * -+ * \param displayIndex The display index -+ * -+ * \return Backing store information -+ */ -+ virtual BackingStoreInfo acquireBackingStore(uint32_t displayIndex) final; -+ -+ /** -+ * Release the backing store lock. -+ */ -+ virtual void releaseBackingStore() final; -+ -+ /** -+ * Take over the display driver from the default Graphics Adapter. -+ * -+ * \return VBox Status Code -+ */ -+ int takeoverDriver(); -+ -+ /** -+ * Hand back the display driver to the default Graphics Adapter. -+ */ -+ void handoverDriver(); -+ -+ /** -+ * Detach all attached displays. -+ */ -+ void detachAllDisplays(); -+ -+private: -+ /** -+ * Update the screen data at the VBox Video Acceleration infrastructure for -+ * the desired display, including a resize, if necessary. -+ * -+ * \param displayIndex The desired display index -+ * \param fResetInputMapping Whether to reset the input mapping or not. -+ * -+ * \return VBox Status Code -+ */ -+ int resizeVBVA(uint32_t displayIndex, bool fResetInputMapping); -+ -+ /** -+ * Enable the VirtualBox Video Acceleration for the desired display. -+ * -+ * \param displayIndex The desired display index -+ * -+ * -+ * \return VBox Status Code -+ */ -+ int enableVBVA(uint32_t displayIndex); -+ -+ /** -+ * Disable the VirtualBox Video Acceleration for the desired display. -+ * -+ * \param displayIndex The desired display index -+ */ -+ void disableVBVA(uint32_t displayIndex); -+ -+ /** -+ * Check that all displays are detached. -+ * -+ * \return true if all displays are detached, false otherwise. -+ */ -+ bool allDisplaysDetached(); -+ -+ /** -+ * The display internal management data structure -+ */ -+ struct Display -+ { -+ VBVAINFOVIEW view; -+ VBVAINFOSCREEN screen; -+ }; -+ -+ PPDMDEVINS pDevIns; -+ unsigned iLUN; -+ PDMIBASE& iBase; -+ const uint32_t u32MonitorCount; -+ -+ PPDMIDISPLAYCONNECTOR pDrv {nullptr}; -+ PPDMIBASE pDrvBase {nullptr}; -+ -+ // Once we have taken over the display this get's true and stays true -+ // until guest reset or reboot. -+ bool ownDisplay {false}; -+ -+ std::vector vram; -+ std::vector displays; -+ -+ std::mutex driverMtx; -+ using DriverGuard = std::lock_guard; -+}; -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpuResource.hpp b/src/VBox/Devices/Graphics/DevVirtioGpuResource.hpp -new file mode 100644 -index 0000000000..f27d57618f ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpuResource.hpp -@@ -0,0 +1,80 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#pragma once -+ -+#include -+#include -+ -+class VirtioGpuResource -+{ -+ const uint32_t uResourceId_; -+ uint32_t uFormat_ {0}; -+ uint32_t uWidth_ {0}; -+ uint32_t uHeight_ {0}; -+ -+ uint32_t uScanoutId_ {0}; -+ -+public: -+ struct MemEntry -+ { -+ const uint64_t uAddr_; // guest physical address -+ const uint32_t uLength_; -+ -+ MemEntry() = delete; -+ MemEntry(uint64_t uAddr, uint32_t uLength) : uAddr_(uAddr), uLength_(uLength) {} -+ }; -+ -+ static constexpr unsigned BYTES_PER_PIXEL {4u}; -+ -+private: -+ std::vector vBacking_ {}; -+ -+public: -+ VirtioGpuResource() = delete; -+ VirtioGpuResource(uint32_t uResourceId) : uResourceId_(uResourceId) {} -+ -+ ~VirtioGpuResource() = default; -+ -+ uint32_t resourceId() const { return uResourceId_; } -+ -+ void format(uint32_t uFormat) { uFormat_ = uFormat; } -+ uint32_t format() const { return uFormat_; } -+ -+ void size(uint32_t uWidth, uint32_t uHeight) -+ { -+ uWidth_ = uWidth; -+ uHeight_ = uHeight; -+ } -+ -+ uint32_t width() const { return uWidth_; } -+ uint32_t height() const { return uHeight_; } -+ -+ void scanoutId(uint32_t uScanoutId) { uScanoutId_ = uScanoutId; } -+ uint32_t scanoutId() { return uScanoutId_; } -+ -+ size_t memNeeded() const { return uWidth_ * uHeight_ * BYTES_PER_PIXEL; } -+ -+ void reserveBacking(size_t sz) { vBacking_.reserve(sz); } -+ void clearBacking() { vBacking_.clear(); } -+ void addBacking(uint64_t uAddr, uint32_t uLenght) { vBacking_.emplace_back(uAddr, uLenght); } -+ -+ MemEntry* getBacking(uint32_t idx) { return &vBacking_.at(idx); } -+ const std::vector& getBacking() { return vBacking_; } -+}; -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpuVBoxStubs.cpp b/src/VBox/Devices/Graphics/DevVirtioGpuVBoxStubs.cpp -new file mode 100644 -index 0000000000..5a5d18c2e2 ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpuVBoxStubs.cpp -@@ -0,0 +1,342 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_GPU -+#include "DevVirtioGpuVBoxStubs.hpp" -+#include "DevVirtioGpu.hpp" -+ -+#include -+#include -+#include -+#include -+ -+#ifdef IN_RING3 -+# include -+#endif -+ -+static DECLCALLBACK(int) devVirtioGpuConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) -+{ -+ // Check that the device instance and device helper structures are compatible. -+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); -+ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ -+ int rc {VINF_SUCCESS}; -+ bool secondaryController {false}; -+ unsigned cMonitorCount {0}; -+ uint32_t u32VRamSize {0}; -+ -+ constexpr char validation[] = "secondaryController" -+ "|MonitorCount" -+ "|VRamSize"; -+ -+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, validation, "Invalid Configuration"); -+ -+ rc = pDevIns->pHlpR3->pfnCFGMQueryBoolDef(pCfg, "secondaryController", &secondaryController, false); -+ if (RT_FAILURE(rc)) { -+ return PDMDEV_SET_ERROR(pDevIns, rc, -+ N_("Condfiguration error: Querying secondaryController asa a bool failed")); -+ } -+ -+ rc = pDevIns->pHlpR3->pfnCFGMQueryU32Def(pCfg, "MonitorCount", &cMonitorCount, 1); -+ if (RT_FAILURE(rc)) { -+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying MonitorCount as uint32_t failed")); -+ } -+ -+ rc = pDevIns->pHlpR3->pfnCFGMQueryU32Def(pCfg, "VRamSize", &u32VRamSize, 32 * _1M); -+ if (RT_FAILURE(rc)) { -+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying VRAM Size as uin32_t failed")); -+ } -+ -+ rc = pThis->init(pDevIns, iInstance, u32VRamSize, cMonitorCount, secondaryController); -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ return VINF_SUCCESS; -+} -+ -+static DECLCALLBACK(int) devVirtioGpuDestruct(PPDMDEVINS pDevIns) -+{ -+ // Check that the device instance and device helper structures are compatible again. -+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); -+ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ -+ int rc {pThis->terminate(pDevIns)}; -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ return VINF_SUCCESS; -+} -+ -+static DECLCALLBACK(void) devVirtioGpuReset(PPDMDEVINS pDevIns) -+{ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ pThis->pDisplayManager->reset(); -+ pThis->pDisplayManager->handoverDriver(); -+} -+ -+static DECLCALLBACK(int) devVirtioGpuAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t) -+{ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ -+ /* we only support iLUN == 0 at the moment */ -+ if (iLUN != 0) { -+ AssertLogRelMsgFailed(("Invalid LUN #%d\n", iLUN)); -+ return VERR_PDM_NO_SUCH_LUN; -+ } -+ -+ int rc {pThis->pDisplayManager->takeoverDriver()}; -+ AssertLogRelReturn(RT_SUCCESS(rc), rc); -+ -+ return VINF_SUCCESS; -+} -+ -+static DECLCALLBACK(void) devVirtioGpuDetach(PPDMDEVINS pDevIns, unsigned, uint32_t) -+{ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ pThis->pDisplayManager->detachAllDisplays(); -+} -+ -+/** -+ * Device registration structure. -+ */ -+extern "C" const PDMDEVREG g_DeviceVirtioGpuDev = { -+ /* .u32Version = */ PDM_DEVREG_VERSION, -+ /* .uReserved0 = */ 0, -+ /* .szName = */ "virtio-gpu", -+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE, -+ /* .fClass = */ PDM_DEVREG_CLASS_GRAPHICS, -+ /* .cMaxInstances = */ 1u, -+ /* .uSharedVersion = */ 42, -+ /* .cbInstanceShared = */ sizeof(VIRTIOGPUDEV), -+ /* .cbInstanceR0 = */ sizeof(VIRTIOGPUDEVCC), -+ /* .cbInstanceRC = */ 0, -+ /* .cMaxPciDevices = */ 1, -+ /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES, -+ /* .pszDescription = */ "Virtio Host GPU.\n", -+ /* .pszRCMod = */ "", -+ /* .pszR0Mod = */ "", -+ /* .pfnConstruct = */ devVirtioGpuConstruct, -+ /* .pfnDestruct = */ devVirtioGpuDestruct, -+ /* .pfnRelocate = */ NULL, -+ /* .pfnMemSetup = */ NULL, -+ /* .pfnPowerOn = */ NULL, -+ /* .pfnReset = */ devVirtioGpuReset, -+ /* .pfnSuspend = */ NULL, -+ /* .pfnResume = */ NULL, -+ /* .pfnAttach = */ devVirtioGpuAttach, -+ /* .pfnDetach = */ devVirtioGpuDetach, -+ /* .pfnQueryInterface. = */ NULL, -+ /* .pfnInitComplete = */ NULL, -+ /* .pfnPowerOff = */ NULL, -+ /* .pfnSoftReset = */ NULL, -+ /* .pfnReserved0 = */ NULL, -+ /* .pfnReserved1 = */ NULL, -+ /* .pfnReserved2 = */ NULL, -+ /* .pfnReserved3 = */ NULL, -+ /* .pfnReserved4 = */ NULL, -+ /* .pfnReserved5 = */ NULL, -+ /* .pfnReserved6 = */ NULL, -+ /* .pfnReserved7 = */ NULL, -+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION}; -+ -+DECLCALLBACK(void) virtioGpuStatusChanged(PVIRTIOCORE pVirtio, PVIRTIOCORECC, uint32_t fDriverOk) -+{ -+ PVIRTIOGPUDEV pThis {RT_FROM_MEMBER(pVirtio, VIRTIOGPUDEV, virtio)}; -+ -+ if (fDriverOk != 0) { -+ int rc = pThis->start(); -+ AssertLogRel(RT_SUCCESS(rc)); -+ } else { -+ int rc = pThis->stop(); -+ AssertLogRel(RT_SUCCESS(rc)); -+ } -+} -+ -+DECLCALLBACK(int) virtioGpuDevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void* pvBuf, uint32_t cbToRead) -+{ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ return pThis->readCap(uOffset, pvBuf, cbToRead); -+} -+ -+DECLCALLBACK(int) virtioGpuDevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void* pvBuf, uint32_t cbToWrite) -+{ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ return pThis->writeCap(uOffset, pvBuf, cbToWrite); -+} -+ -+DECLCALLBACK(void) virtioGpuVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE, uint16_t uVirtqNbr) -+{ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ pThis->wakeupWorker(uVirtqNbr); -+} -+ -+DECLCALLBACK(void) -+virtioGpuDisplayChanged(PPDMDEVINS pDevIns, uint32_t numDisplays, VMMDevDisplayDef* displayDefs) -+{ -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ pThis->displayChanged(numDisplays, displayDefs); -+} -+ -+DECLCALLBACK(void*) virtioGpuQueryInterface(PPDMIBASE pInterface, const char* pszIID) -+{ -+ PPDMDEVINS pDevIns {PDMIBASE_2_PDMDEV(pInterface)}; -+ PVIRTIOGPUDEV pThis {PDMDEVINS_2_DATA(pDevIns, PVIRTIOGPUDEV)}; -+ LogRel8(("%s: virtioGpuQueryInterface.\n", pThis->szInst.c_str())); -+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIVIRTIOGPUPORT, &pThis->IVirtioGpuPort); -+ return nullptr; -+} -+ -+DECLCALLBACK(void*) virtioGpuPortQueryInterface(PPDMIBASE pInterface, const char* pszIID) -+{ -+ PVIRTIOGPUDEV pThis {RT_FROM_MEMBER(pInterface, VIRTIOGPUDEV, IBase)}; -+ LogRel8(("%s: virtioGpuPortQueryInterface.\n", pThis->szInst.c_str())); -+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase); -+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPort); -+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThis->IVBVACallbacks); -+ return nullptr; -+} -+ -+static PVIRTIOGPUDEV virtioGpuFromPPDMIDISPLAYPORT(PPDMIDISPLAYPORT pInterface) -+{ -+ return reinterpret_cast(reinterpret_cast(pInterface) - RT_OFFSETOF(VIRTIOGPUDEV, IPort)); -+} -+ -+DECLCALLBACK(void) virtioGpuPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortSetRenderVRAM.\n", pThis->szInst.c_str())); -+} -+ -+DECLCALLBACK(int) virtioGpuUpdateDisplay(PPDMIDISPLAYPORT pInterface) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuUpdateDisplay.\n", pThis->szInst.c_str())); -+ return VINF_SUCCESS; -+} -+ -+DECLCALLBACK(int) virtioGpuPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortUpdateDisplayAll.\n", pThis->szInst.c_str())); -+ return VINF_SUCCESS; -+} -+ -+DECLCALLBACK(int) -+virtioGpuPortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t* pcBits, uint32_t* pcx, uint32_t* pcy) -+{ -+ PVIRTIOGPUDEV pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortQueryVideoMode. pcBits: %u, pcx: %u, pcy: %u.\n", pThis->szInst.c_str(), *pcBits, *pcx, -+ *pcy)); -+ -+ if (!pcBits) { -+ return VERR_INVALID_PARAMETER; -+ } -+ -+ *pcBits = 0; -+ -+ // CYBER-TODO: This should not always be scanout 0 -+ // When we have figured out how to handle multiple VBox-Windows, we have to -+ // figure out how to get the index of the scanout here. -+ auto maybeScanout {pThis->pCmdHandler->getCScanout(0)}; -+ if (not maybeScanout.has_value()) { -+ return VINF_SUCCESS; -+ } -+ -+ const auto currentScanout {maybeScanout->get()}; -+ if (pcx) { -+ *pcx = currentScanout.uCurrentWidth; -+ } -+ -+ if (pcy) { -+ *pcy = currentScanout.uCurrentHeight; -+ } -+ -+ return VINF_SUCCESS; -+} -+ -+DECLCALLBACK(int) virtioGpuPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortSetRefreshRate.\n", pThis->szInst.c_str())); -+ return VINF_SUCCESS; -+} -+ -+DECLCALLBACK(int) virtioGpuPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t**, size_t*, uint32_t*, uint32_t*) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortTakeScreenshot.\n", pThis->szInst.c_str())); -+ return VINF_SUCCESS; -+} -+ -+DECLCALLBACK(void) virtioGpuPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t*) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortFreeScreenshot.\n", pThis->szInst.c_str())); -+} -+ -+DECLCALLBACK(void) virtioGpuPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t, int32_t, uint32_t, uint32_t) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortUpdateDisplayRect.\n", pThis->szInst.c_str())); -+} -+ -+DECLCALLBACK(int) -+virtioGpuPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void*, uint32_t, uint32_t, uint32_t, uint32_t) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortDisplayBlt.\n", pThis->szInst.c_str())); -+ return VINF_SUCCESS; -+} -+ -+DECLCALLBACK(int) -+virtioGpuPortCopyRect(PPDMIDISPLAYPORT pInterface, uint32_t, uint32_t, const uint8_t*, int32_t, int32_t, uint32_t, -+ uint32_t, uint32_t, uint32_t, uint8_t*, int32_t, int32_t, uint32_t, uint32_t, uint32_t, uint32_t) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: virtioGpuPortCopyRect.\n", pThis->szInst.c_str())); -+ return VINF_SUCCESS; -+} -+ -+DECLCALLBACK(void) -+vmsvgavirtioGpuPortSetViewport(PPDMIDISPLAYPORT pInterface, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: SetViewport\n\n\n\n\n", pThis->szInst.c_str())); -+} -+ -+DECLCALLBACK(int) -+vbvavirtioGpuPortSendModeHint(PPDMIDISPLAYPORT pInterface, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, -+ uint32_t, uint32_t) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: vbvavirtioGpuPortSendModeHint.\n", pThis->szInst.c_str())); -+ return VINF_SUCCESS; -+} -+ -+DECLCALLBACK(void) vbvavirtioGpuPortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool, bool) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: vbvavirtioGpuPortReportHostCursorCapabilities.\n", pThis->szInst.c_str())); -+} -+ -+DECLCALLBACK(void) vbvavirtioGpuPortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t, uint32_t, bool) -+{ -+ [[maybe_unused]] auto pThis {virtioGpuFromPPDMIDISPLAYPORT(pInterface)}; -+ LogRel8(("%s: vbvavirtioGpuPortReportHostCursorPosition.\n", pThis->szInst.c_str())); -+} -diff --git a/src/VBox/Devices/Graphics/DevVirtioGpuVBoxStubs.hpp b/src/VBox/Devices/Graphics/DevVirtioGpuVBoxStubs.hpp -new file mode 100644 -index 0000000000..fd6a56241c ---- /dev/null -+++ b/src/VBox/Devices/Graphics/DevVirtioGpuVBoxStubs.hpp -@@ -0,0 +1,71 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#pragma once -+ -+#include -+#include -+ -+#include "../VirtIO/VirtioCore.h" -+ -+#include -+ -+/* -+ * VIRTIOCORER3 -+ */ -+ -+/** VIRTIOCORER3::pfnStatusChanged */ -+DECLCALLBACK(void) virtioGpuStatusChanged(PVIRTIOCORE pVirtio, PVIRTIOCORECC, uint32_t fDriverOk); -+ -+/** VIRTIOCORER3::pfnDevCapRead */ -+DECLCALLBACK(int) virtioGpuDevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void* pvBuf, uint32_t cbToRead); -+ -+/** VIRTIOCORER3::pfnDevCapWrite */ -+DECLCALLBACK(int) virtioGpuDevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void* pvBuf, uint32_t cbToWrite); -+ -+/** VIRTIOCORER3::pfnVirtqNotified */ -+DECLCALLBACK(void) virtioGpuVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE, uint16_t uVirtqNbr); -+ -+DECLCALLBACK(void) -+virtioGpuDisplayChanged(PPDMDEVINS pDevIns, uint32_t numDisplays, VMMDevDisplayDef* displayDefs); -+ -+DECLCALLBACK(void*) virtioGpuQueryInterface(PPDMIBASE pInterface, const char* pszIID); -+ -+DECLCALLBACK(void) virtioGpuReset(PPDMDEVINS); -+DECLCALLBACK(int) virtioGpuAttach(PPDMDEVINS, unsigned, uint32_t); -+DECLCALLBACK(void) virtioGpuDetach(PPDMDEVINS, unsigned, uint32_t); -+DECLCALLBACK(void*) virtioGpuPortQueryInterface(PPDMIBASE, const char*); -+DECLCALLBACK(void) virtioGpuPortSetRenderVRAM(PPDMIDISPLAYPORT, bool); -+DECLCALLBACK(int) virtioGpuUpdateDisplay(PPDMIDISPLAYPORT); -+DECLCALLBACK(int) virtioGpuPortUpdateDisplayAll(PPDMIDISPLAYPORT, bool); -+DECLCALLBACK(int) virtioGpuPortQueryVideoMode(PPDMIDISPLAYPORT, uint32_t*, uint32_t*, uint32_t*); -+DECLCALLBACK(int) virtioGpuPortSetRefreshRate(PPDMIDISPLAYPORT, uint32_t); -+DECLCALLBACK(int) virtioGpuPortTakeScreenshot(PPDMIDISPLAYPORT, uint8_t**, size_t*, uint32_t*, uint32_t*); -+DECLCALLBACK(void) virtioGpuPortFreeScreenshot(PPDMIDISPLAYPORT, uint8_t*); -+DECLCALLBACK(void) virtioGpuPortUpdateDisplayRect(PPDMIDISPLAYPORT, int32_t, int32_t, uint32_t, uint32_t); -+DECLCALLBACK(int) virtioGpuPortDisplayBlt(PPDMIDISPLAYPORT, const void*, uint32_t, uint32_t, uint32_t, uint32_t); -+DECLCALLBACK(int) -+virtioGpuPortCopyRect(PPDMIDISPLAYPORT, uint32_t, uint32_t, const uint8_t*, int32_t, int32_t, uint32_t, uint32_t, -+ uint32_t, uint32_t, uint8_t*, int32_t, int32_t, uint32_t, uint32_t, uint32_t, uint32_t); -+DECLCALLBACK(void) vmsvgavirtioGpuPortSetViewport(PPDMIDISPLAYPORT, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); -+DECLCALLBACK(int) -+vbvavirtioGpuPortSendModeHint(PPDMIDISPLAYPORT, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, -+ uint32_t); -+DECLCALLBACK(void) vbvavirtioGpuPortReportHostCursorCapabilities(PPDMIDISPLAYPORT, bool, bool); -+DECLCALLBACK(void) vbvavirtioGpuPortReportHostCursorPosition(PPDMIDISPLAYPORT, uint32_t, uint32_t, bool); -diff --git a/src/VBox/Devices/Makefile.kmk b/src/VBox/Devices/Makefile.kmk -index 90f01dbd20..69e96a4d82 100644 ---- a/src/VBox/Devices/Makefile.kmk -+++ b/src/VBox/Devices/Makefile.kmk -@@ -197,6 +197,10 @@ if !defined(VBOX_ONLY_EXTPACKS) && "$(intersects $(KBUILD_TARGET_ARCH),$(VBOX_SU - EFI/DevFlash.cpp \ - EFI/FlashCore.cpp \ - Graphics/DevVGA.cpp \ -+ Graphics/DevVirtioGpuCmdHandler.cpp \ -+ Graphics/DevVirtioGpu.cpp \ -+ Graphics/DevVirtioGpuDisplayManager.cpp \ -+ Graphics/DevVirtioGpuVBoxStubs.cpp \ - Storage/DevATA.cpp \ - PC/DevPit-i8254.cpp \ - PC/DevPIC.cpp \ -@@ -259,6 +263,12 @@ if !defined(VBOX_ONLY_EXTPACKS) && "$(intersects $(KBUILD_TARGET_ARCH),$(VBOX_SU - Bus/DevVfio.cpp_CXXFLAGS.linux += $(CYBERUS_CXX_FLAGS) - Bus/VfioDevice.cpp_CXXFLAGS.linux += $(CYBERUS_CXX_FLAGS) - -+ # VirtioGPU -+ VBoxDD_DEFS += $(VMM_COMMON_DEFS) -+ Graphics/DevVirtioGpuCmdHandler.cpp_CXXFLAGS.linux += $(CYBERUS_CXX_FLAGS) -+ Graphics/DevVirtioGpu.cpp_CXXFLAGS.linux += $(CYBERUS_CXX_FLAGS) -+ Graphics/DevVirtioGpuVBoxStubs.cpp_CXXFLAGS.linux += $(CYBERUS_CXX_FLAGS) -+ - ifn1of ($(KBUILD_TARGET), darwin) - VBoxDD_SOURCES += Storage/HBDMgmt-generic.cpp - endif -diff --git a/src/VBox/Devices/VirtIO/VirtioCore.cpp b/src/VBox/Devices/VirtIO/VirtioCore.cpp -index a0deddd698..669cf335c0 100644 ---- a/src/VBox/Devices/VirtIO/VirtioCore.cpp -+++ b/src/VBox/Devices/VirtIO/VirtioCore.cpp -@@ -1451,7 +1451,7 @@ static int virtioNudgeGuest(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint8_t uCa - - if (!pVirtio->fMsiSupport) - { -- pVirtio->uISR |= uCause; -+ pVirtio->uISR = (VIRTIO_ISR_DEVICE_CONFIG == uCause) ? 0x3 : uCause; - PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_HIGH); - } - else if (uMsixVector != VIRTIO_MSI_NO_VECTOR) -diff --git a/src/VBox/Devices/build/VBoxDD.cpp b/src/VBox/Devices/build/VBoxDD.cpp -index 8e51da7c4f..ea4353a2c7 100644 ---- a/src/VBox/Devices/build/VBoxDD.cpp -+++ b/src/VBox/Devices/build/VBoxDD.cpp -@@ -254,6 +254,9 @@ extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t - if (RT_FAILURE(rc)) - return rc; - #endif -+ rc = pCallbacks->pfnRegister(pCallbacks, &g_DeviceVirtioGpuDev); -+ if (RT_FAILURE(rc)) -+ return rc; - - return VINF_SUCCESS; - } -diff --git a/src/VBox/Devices/build/VBoxDD.h b/src/VBox/Devices/build/VBoxDD.h -index 0927d8a327..fbd4f800c9 100644 ---- a/src/VBox/Devices/build/VBoxDD.h -+++ b/src/VBox/Devices/build/VBoxDD.h -@@ -109,6 +109,7 @@ extern const PDMDEVREG g_DevicePciRaw; - #endif - extern const PDMDEVREG g_DeviceVfioDev; - extern const PDMDEVREG g_DeviceGIMDev; -+extern const PDMDEVREG g_DeviceVirtioGpuDev; - extern const PDMDEVREG g_DeviceLPC; - #ifdef VBOX_WITH_VIRTUALKD - extern const PDMDEVREG g_DeviceVirtualKD; -diff --git a/src/VBox/Devices/testcase/Makefile.kmk b/src/VBox/Devices/testcase/Makefile.kmk -index a65768b802..288199e3bc 100644 ---- a/src/VBox/Devices/testcase/Makefile.kmk -+++ b/src/VBox/Devices/testcase/Makefile.kmk -@@ -70,12 +70,18 @@ ifeq ($(KBUILD_TARGET),$(KBUILD_HOST)) - ifeq ($(filter-out x86.x86 amd64.amd64 x86.amd64, $(KBUILD_TARGET_ARCH).$(KBUILD_HOST_ARCH)),) - OTHERS += \ - $(VBOX_DEVICES_TEST_OUT_DIR)/tstDeviceStructSize.run -+ if defined(VBOX_WITH_SUPERNOVA_UNITTESTS) -+ OTHERS += $(VBOX_DEVICES_TEST_OUT_DIR)/tstVirtioGpuCmdHandling.run -+ endif - endif - endif - - # The normal testing pass. --TESTING += \ -- $(VBOX_DEVICES_TEST_OUT_DIR)/tstDeviceStructSize.run -+TESTING += $(VBOX_DEVICES_TEST_OUT_DIR)/tstDeviceStructSize.run -+ -+if defined(VBOX_WITH_SUPERNOVA_UNITTESTS) -+ TESTING += $(VBOX_DEVICES_TEST_OUT_DIR)/tstVirtioGpuCmdHandling.run -+endif - - ifdef VBOX_WITH_RAW_MODE - # -@@ -128,6 +134,27 @@ ifdef VBOX_WITH_RAW_MODE - endif - tstDeviceStructSize_INCS += $(VBOX_PATH_VMM_DEVICES_SRC) - -+if defined(VBOX_WITH_SUPERNOVA_UNITTESTS) -+PROGRAMS += tstVirtioGpuCmdHandling -+endif -+ -+tstVirtioGpuCmdHandling_TEMPLATE = VBOXR3AUTOTST -+tstVirtioGpuCmdHandling_DEFS = $(VBOX_DEVICES_TESTS_FEATURES) -+tstVirtioGpuCmdHandling_INCS = \ -+ $(VBOX_PATH_DEVICES_SRC)/build \ -+ $(VBOX_PATH_DEVICES_SRC)/Bus \ -+ $(VBOX_DEVICES_TEST_OUT_DIR) \ -+ $(VBOX_GRAPHICS_INCS) \ -+ $(VBoxDD_INCS) -+tstVirtioGpuCmdHandling_SOURCES = \ -+ ../Graphics/DevVirtioGpuCmdHandler.cpp \ -+ tstVirtioGpuCmdHandling.cpp -+tstVirtioGpuCmdHandling_LIBS = \ -+ $(VBOX_LIB_RUNTIME_STATIC) -+tstVirtioGpuCmdHandling_CLEAN = \ -+ $(VBOX_DEVICES_TEST_OUT_DIR)/tstVirtioGpuCmdHandling.run -+tstVirtioGpuCmdHandling_INCS += $(VBOX_PATH_VMM_DEVICES_SRC) -+ - # - # Run rule for tstDeviceStructSize. - # -@@ -149,8 +176,14 @@ $(VBOX_DEVICES_TEST_OUT_DIR)/tstDeviceStructSize.run: $$(tstDeviceStructSize_1_S - $^ - $(QUIET)$(APPEND) "$@" "done" - -+$(VBOX_DEVICES_TEST_OUT_DIR)/tstVirtioGpuCmdHandling.run: $$(tstVirtioGpuCmdHandling_1_STAGE_TARGET) | $$(dir $$@) -+ $(QUIET)$(RM) -f $@ -+ $^ -+ $(QUIET)$(APPEND) "$@" "done" -+ - # alias for the struct test. - run-struct-tests: $(VBOX_DEVICES_TEST_OUT_DIR)/tstDeviceStructSize.run -+run-supernova-tests: $(VBOX_DEVICES_TEST_OUT_DIR)/tstVirtioGpuCmdHandling.run - - - include $(FILE_KBUILD_SUB_FOOTER) -diff --git a/src/VBox/Devices/testcase/tstVirtioGpuAdapter.hpp b/src/VBox/Devices/testcase/tstVirtioGpuAdapter.hpp -new file mode 100644 -index 0000000000..9e23a733cf ---- /dev/null -+++ b/src/VBox/Devices/testcase/tstVirtioGpuAdapter.hpp -@@ -0,0 +1,181 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#include "../Graphics/DevVirtioGpuCmdHandler.hpp" -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+static constexpr uint32_t TST_VIOGPU_MAX_SCANOUTS {2u}; -+ -+class tstVirtioAdapter final : public VirtioGpuCmdHandler::VirtioAdapter -+{ -+public: -+ uint8_t* sendBuf_ {nullptr}; // During each virtqBufDrain, we will read from this pointer into pv -+ uint8_t* recvBuf_ {nullptr}; // During each virtqBufPut, we will write to this pointer -+ -+ template -+ void prepareCommand(SendType* send, ReceiveType* recv, uint16_t uVirtq, PVIRTQBUF pVirtqBuf) -+ { -+ prepareCommand(send, sizeof(SendType), recv, sizeof(ReceiveType), uVirtq, pVirtqBuf); -+ } -+ -+ void prepareCommand(void* sendBuf, size_t sendSz, void* recvBuf, size_t recvSz, uint16_t uVirtq, PVIRTQBUF pVirtqBuf) -+ { -+ sendBuf_ = reinterpret_cast(sendBuf); -+ pVirtqBuf->cbPhysSend = sendSz; -+ recvBuf_ = reinterpret_cast(recvBuf); -+ pVirtqBuf->cbPhysReturn = recvSz; -+ pVirtqBuf->uVirtq = uVirtq; -+ } -+ -+ void virtqBufDrain(PVIRTQBUF pVirtqBuf, void *pv, size_t cb) final -+ { -+ REQUIRE(pVirtqBuf != nullptr); -+ REQUIRE(pv != nullptr); -+ REQUIRE(cb != 0); -+ -+ // The cmdHandler has to check wether the size of the src buffer is sufficient for the drain-request -+ REQUIRE(pVirtqBuf->cbPhysSend >= cb); -+ std::memcpy(pv, sendBuf_, cb); -+ pVirtqBuf->cbPhysSend -= cb; -+ sendBuf_ += cb; -+ } -+ -+ void virtqBufPut(PVIRTQBUF pVirtqBuf, void *pv, size_t cb) final -+ { -+ REQUIRE(pVirtqBuf != nullptr); -+ REQUIRE(pv != nullptr); -+ REQUIRE(cb != 0); -+ -+ // The cmdHandler has to check wether the size of the dst buffer is sufficient for the put-request -+ REQUIRE(pVirtqBuf->cbPhysReturn >= cb); -+ std::memcpy(recvBuf_, pv, cb); -+ pVirtqBuf->cbPhysReturn -= cb; -+ recvBuf_ += cb; -+ } -+ -+ void virtqSyncRings(PVIRTQBUF pVirtqBuf) final -+ { -+ REQUIRE(pVirtqBuf != 0); -+ } -+}; -+ -+class tstDisplayAdapter final : public VirtioGpuCmdHandler::displayAdapter -+{ -+public: -+ std::vector framebuf; -+ uint32_t displayIdx {0}; -+ bool fAttached {false}; -+ bool fFlushed {false}; -+ uint32_t uCurrentWidth {virtioGpu::INITIAL_WIDTH}; -+ uint32_t uCurrentHeight {virtioGpu::INITIAL_HEIGHT}; -+ -+ tstDisplayAdapter() = default; -+ -+ void reset() -+ { -+ framebuf.clear(); -+ fAttached = false; -+ fFlushed = false; -+ uCurrentWidth = virtioGpu::INITIAL_WIDTH; -+ uCurrentHeight = virtioGpu::INITIAL_HEIGHT; -+ } -+ -+ void resize(uint32_t uWidth, uint32_t uHeight) final -+ { -+ uCurrentWidth = uWidth; -+ uCurrentHeight = uHeight; -+ -+ framebuf.resize(cbFrameBuffer()); -+ } -+ -+ std::tuple size() final -+ { -+ return std::make_tuple(uCurrentWidth, uCurrentHeight); -+ } -+ -+ void attachDisplay(unsigned iLUN) final -+ { -+ REQUIRE(iLUN == displayIdx); -+ fAttached = true; -+ } -+ -+ void detachDisplay(unsigned iLUN) final -+ { -+ REQUIRE(iLUN == displayIdx); -+ fAttached = false; -+ } -+ -+ bool isAttachedToDisplay() final -+ { -+ return fAttached; -+ } -+ -+ void flush(uint32_t /*uWidth*/, uint32_t /*uHeight*/) final -+ { -+ fFlushed = true; -+ } -+ -+ void* pFrameBuffer() final { return framebuf.data(); } -+ size_t cbFrameBuffer() final { return uCurrentWidth * uCurrentHeight * virtioGpuResource::BYTES_PER_PIXEL; } -+}; -+ -+class tstDisplayManager final : public VirtioGpuCmdHandler::DisplayManager -+{ -+ std::array displayAdapters; -+public: -+ tstDisplayManager() -+ { -+ uint32_t displayIdx {0u}; -+ for (auto& displayAdapterOne : displayAdapters) { -+ displayAdapterOne.displayIdx = displayIdx; -+ displayIdx++; -+ } -+ } -+ -+ tstDisplayAdapter* display(uint32_t idx) final -+ { -+ if (idx > TST_VIOGPU_MAX_SCANOUTS-1) { -+ return nullptr; -+ } -+ return &displayAdapters.at(idx); -+ } -+}; -+ -+class tstMemoryAdapter final : public VirtioGpuCmdHandler::MemoryAdapter -+{ -+public: -+ VecMappings mapGCPhys2HCVirt(const vecMemEntries& vBacking) final -+ { -+ vecMappings vMapping; -+ for (const auto& backing : vBacking) { -+ void* uAddr {reinterpret_cast(backing.uAddr_)}; -+ vMapping.emplace_back(uAddr, backing.uLength_, nullptr); -+ } -+ return vMapping; -+ } -+ -+ void releaseMappings(const vecMappings& /*vMapping*/) final { return ; } -+}; -diff --git a/src/VBox/Devices/testcase/tstVirtioGpuCmdHandling.cpp b/src/VBox/Devices/testcase/tstVirtioGpuCmdHandling.cpp -new file mode 100644 -index 0000000000..a10d4e1aaf ---- /dev/null -+++ b/src/VBox/Devices/testcase/tstVirtioGpuCmdHandling.cpp -@@ -0,0 +1,450 @@ -+/* -+ * Copyright (C) Cyberus Technology GmbH. -+ * -+ * This program is free software: you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation, either version 3 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ * -+ * SPDX-License-Identifier: GPL-3.0-or-later -+ */ -+ -+#include -+#include -+#include -+ -+#define CATCH_CONFIG_MAIN -+#include -+ -+#undef LOG_GROUP -+#include "../Graphics/DevVirtioGpuDefinitions.hpp" -+#include "../Graphics/DevVirtioGpuCmdHandler.hpp" -+#include "tstVirtioGpuAdapter.hpp" -+ -+#include -+ -+// virtioAdapter and memoryAdapter both have no state, thus we can reuse them -+static tstVirtioAdapter virtioAdapter; -+static tstMemoryAdapter memoryAdapter; -+ -+static VIRTQBUF virtqBuf; -+ -+static constexpr uint32_t RESOURCE_ID_ONE {1u}; -+static constexpr uint32_t RESOURCE_ID_TWO {2u}; -+ -+static constexpr uint32_t SCANOUT_ID_ONE {0u}; -+static constexpr uint32_t SCANOUT_ID_TWO {1u}; -+ -+static constexpr uint32_t RESOURCE_WIDTH {1920u}; -+static constexpr uint32_t RESOURCE_HEIGHT {1080u}; -+static constexpr uint32_t RESIZED_WIDTH {800u}; -+static constexpr uint32_t RESIZED_HEIGHT {600u}; -+ -+static constexpr uint32_t NUM_BACKINGS {4u}; -+static constexpr size_t BACKING_SIZE {X86_PAGE_SIZE}; -+static constexpr size_t SIZE_FRAMEBUFFER {NUM_BACKINGS * BACKING_SIZE}; -+ -+static constexpr size_t ATTACH_BACKING_STRUCT_SIZE {sizeof(virtioGpu::resourceAttachBacking) + NUM_BACKINGS * sizeof(virtioGpu::resourceMemEntry)}; -+ -+TEST_CASE("handler returns out-of-memory error if request-buffer is too small") -+{ -+ tstDisplayManager displayManager; -+ VirtioGpuCmdHandler handler(virtioAdapter, displayManager, memoryAdapter, TST_VIOGPU_MAX_SCANOUTS, false); -+ virtioGpu::ctrlHdr recvHdr; -+ -+ virtqBuf.cbPhysReturn = sizeof(recvHdr); -+ virtqBuf.uVirtq = virtioGpu::virtqIdx::CONTROLQ; -+ virtioAdapter.recvBuf_ = reinterpret_cast(&recvHdr); -+ -+ handler.handleBuffer(&virtqBuf); -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::ERR_OUT_OF_MEMORY); -+} -+ -+TEST_CASE("handler returns unspec error if the ctrl-type is unknown") -+{ -+ tstDisplayManager displayManager; -+ VirtioGpuCmdHandler handler(virtioAdapter, displayManager, memoryAdapter, TST_VIOGPU_MAX_SCANOUTS, false); -+ virtioGpu::ctrlHdr recvHdr; -+ -+ // GET_DISPLAY_INFO is the command with the lowest value, thus GET_DISPLAY_INFO is an invalid command. -+ virtioGpu::ctrlHdr sendHdr {virtioGpu::ctrlType::cmd::GET_DISPLAY_INFO - 1}; -+ -+ virtioAdapter.prepareCommand(&sendHdr, &recvHdr, virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::ERR_UNSPEC); -+} -+ -+TEST_CASE("handler returns unspec error if a command is in the wrong queue") -+{ -+ tstDisplayManager displayManager; -+ VirtioGpuCmdHandler handler(virtioAdapter, displayManager, memoryAdapter, TST_VIOGPU_MAX_SCANOUTS, false); -+ -+ for (uint32_t cmd {virtioGpu::ctrlType::cmd::GET_DISPLAY_INFO}; cmd < virtioGpu::ctrlType::cmd::RESOURCE_DETACH_BACKING; cmd++) { -+ virtioGpu::ctrlHdr sendHdr {cmd}; -+ virtioGpu::ctrlHdr recvHdr; -+ virtioAdapter.prepareCommand(&sendHdr, &recvHdr, virtioGpu::virtqIdx::CURSORQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::ERR_UNSPEC); -+ } -+ -+ { -+ virtioGpu::ctrlHdr sendHdr {virtioGpu::ctrlType::cmd::GET_EDID}; -+ virtioGpu::ctrlHdr recvHdr; -+ virtioAdapter.prepareCommand(&sendHdr, &recvHdr, virtioGpu::virtqIdx::CURSORQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::ERR_UNSPEC); -+ } -+ -+ for (uint32_t cmd {virtioGpu::ctrlType::cmd::UPDATE_CURSOR}; cmd < virtioGpu::ctrlType::cmd::MOVE_CURSOR; cmd++) { -+ virtioGpu::ctrlHdr sendHdr {cmd}; -+ virtioGpu::ctrlHdr recvHdr; -+ virtioAdapter.prepareCommand(&sendHdr, &recvHdr, virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::ERR_UNSPEC); -+ } -+} -+ -+SCENARIO("The command handler respects the attachDisplayLater flag") -+{ -+ GIVEN("A display adapter and a GET_DISPLAY_INFO command") { -+ tstDisplayManager displayManager; -+ virtioGpu::ctrlHdr sendHdr {virtioGpu::ctrlType::cmd::GET_DISPLAY_INFO}; -+ virtioGpu::respDisplayInfo displayInfo; -+ virtioAdapter.prepareCommand(&sendHdr, sizeof(sendHdr), &displayInfo, virtioGpu::respDisplayInfo::size(TST_VIOGPU_MAX_SCANOUTS), virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ -+ WHEN("The command handler is created with attachDisplayLater set to false and GET_DISPLAY_INFO is called") { -+ VirtioGpuCmdHandler handler(virtioAdapter, displayManager, memoryAdapter, TST_VIOGPU_MAX_SCANOUTS, false); -+ handler.handleBuffer(&virtqBuf); -+ -+ THEN("Display 0 is enabled, has the initial resolution and is attached") { -+ REQUIRE(displayInfo.hdr.uType == virtioGpu::ctrlType::resp::OK_DISPLAY_INFO); -+ REQUIRE(displayInfo.pmodes[0].enabled != 0); -+ REQUIRE(displayInfo.pmodes[0].r.width == virtioGpu::INITIAL_WIDTH); -+ REQUIRE(displayInfo.pmodes[0].r.height == virtioGpu::INITIAL_HEIGHT); -+ REQUIRE(displayManager.display(0)->fAttached == true); -+ } -+ } -+ -+ WHEN("The command handler is created with attachDisplayLater set to true and GET_DISPLAY_INFO is called") { -+ VirtioGpuCmdHandler handler(virtioAdapter, displayManager, memoryAdapter, TST_VIOGPU_MAX_SCANOUTS, true); -+ handler.handleBuffer(&virtqBuf); -+ -+ THEN("Display 0 is enabled and has the initial resolution, but the display is not attached") { -+ // We do this because if a driver attaches later to the virtio-gpu but immerdiately asks for the display info, -+ // the driver should still see all available scanouts. -+ REQUIRE(displayInfo.hdr.uType == virtioGpu::ctrlType::resp::OK_DISPLAY_INFO); -+ REQUIRE(displayInfo.pmodes[0].enabled != 0); -+ REQUIRE(displayInfo.pmodes[0].r.width == virtioGpu::INITIAL_WIDTH); -+ REQUIRE(displayInfo.pmodes[0].r.height == virtioGpu::INITIAL_HEIGHT); -+ REQUIRE(displayManager.display(0)->fAttached == false); -+ } -+ } -+ } -+} -+ -+SCENARIO("Creation and deletion of resources is handled correctly") { -+ GIVEN("A fresh command handler") { -+ tstDisplayManager displayManager; -+ VirtioGpuCmdHandler handler(virtioAdapter, displayManager, memoryAdapter, TST_VIOGPU_MAX_SCANOUTS, false); -+ -+ WHEN("A resource with ID 0 should be allocated") { -+ virtioGpu::ctrlHdr recvHdr; -+ virtioGpu::resourceCreate2d createResource {0u}; -+ -+ virtioAdapter.prepareCommand(&createResource, &recvHdr, virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ -+ THEN("ERR_INVALID_RESOURCE_ID is returned.") { -+ // The driver disables a scanout be using the resource ID 0 in SET_SCANOUT. -+ // Thus the cmd handler should not allocate resources with ID 0. -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::ERR_INVALID_RESOURCE_ID); -+ } -+ } -+ -+ WHEN("A resource with a valid ID is allocated") { -+ virtioGpu::ctrlHdr recvHdr; -+ virtioGpu::resourceCreate2d createResource {RESOURCE_ID_ONE, RESOURCE_WIDTH, RESOURCE_HEIGHT}; -+ -+ virtioAdapter.prepareCommand(&createResource, &recvHdr, virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ -+ THEN("OK_NODATA is returned.") { -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::OK_NODATA); -+ } -+ -+ AND_WHEN("A resource with the same ID is allocated") { -+ recvHdr.uType = 0; -+ -+ virtioAdapter.prepareCommand(&createResource, &recvHdr, virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ -+ THEN("ERR_INVALID_RESOURCE_ID is returned.") { -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::ERR_INVALID_RESOURCE_ID); -+ } -+ } -+ -+ AND_WHEN("The resource is deleted") { -+ recvHdr.uType = 0; -+ virtioGpu::resourceUnref unrefResource {RESOURCE_ID_ONE}; -+ -+ virtioAdapter.prepareCommand(&unrefResource, &recvHdr, virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ -+ THEN("OK_NODATA is returned.") { -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::OK_NODATA); -+ } -+ -+ AND_WHEN("The resource is deleted again") { -+ recvHdr.uType = 0; -+ -+ virtioAdapter.prepareCommand(&unrefResource, &recvHdr, virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ -+ THEN("ERR_INVALID_RESOURCE_ID is returned") { -+ REQUIRE(recvHdr.uType == virtioGpu::ctrlType::resp::ERR_INVALID_RESOURCE_ID); -+ } -+ } -+ } -+ } -+ } -+} -+ -+SCENARIO("Complex tests") { -+ GIVEN("A command handler with two resources with IDs 1 and 2 and attached backings to both resources") { -+ tstDisplayManager displayManager; -+ VirtioGpuCmdHandler handler(virtioAdapter, displayManager, memoryAdapter, TST_VIOGPU_MAX_SCANOUTS, false); -+ -+ // 'Simple', because I just put in the two pointers -+ auto runSimpleCommand = [&handler](auto* pSend, auto* pRecv) -> void { -+ pRecv->hdr.uType = 0; -+ virtioAdapter.prepareCommand(pSend, pRecv, virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ }; -+ -+ auto runSimpleCommandAndCheck = [&handler, &runSimpleCommand](auto* pSend, auto* pRecv, -+ virtioGpu::ctrlType::resp response = virtioGpu::ctrlType::resp::OK_NODATA) -> void { -+ runSimpleCommand(pSend, pRecv); -+ REQUIRE(pRecv->hdr.uType == response); -+ }; -+ -+ // 'Complex', because I have to provide the size -+ auto runComplexCommand = [&handler](void* pSend, size_t cbSend, auto* pRecv) -> void { -+ pRecv->hdr.uType = 0; -+ virtioAdapter.prepareCommand(pSend, cbSend, pRecv, sizeof(*pRecv), virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ }; -+ -+ auto runComplexCommandAndCheck = [&handler, &runComplexCommand](void* pSend, size_t cbSend, auto* pRecv, -+ virtioGpu::ctrlType::resp response = virtioGpu::ctrlType::resp::OK_NODATA) -> void { -+ runComplexCommand(pSend, cbSend, pRecv); -+ REQUIRE(pRecv->hdr.uType == response); -+ }; -+ -+ struct { virtioGpu::ctrlHdr hdr; } recvHdr; -+ virtioGpu::respDisplayInfo recvDisplayInfo; -+ -+ virtioGpu::ctrlHdr getDisplayInfo {virtioGpu::ctrlType::cmd::GET_DISPLAY_INFO}; -+ -+ virtioGpu::resourceCreate2d createResourceOne {RESOURCE_ID_ONE, RESOURCE_WIDTH, RESOURCE_HEIGHT}; -+ virtioGpu::setScanout setScanoutOne {SCANOUT_ID_ONE, RESOURCE_ID_ONE, RESOURCE_WIDTH, RESOURCE_HEIGHT}; -+ virtioGpu::setScanout disableScanoutOne {SCANOUT_ID_ONE, 0u}; -+ virtioGpu::transferToHost2d transfer2HostOne {RESOURCE_ID_ONE, RESOURCE_WIDTH, RESOURCE_HEIGHT}; -+ virtioGpu::resourceDetachBacking detachBackingOne {RESOURCE_ID_ONE}; -+ -+ virtioGpu::resourceCreate2d createResourceTwo {RESOURCE_ID_TWO, RESOURCE_WIDTH, RESOURCE_HEIGHT}; -+ virtioGpu::setScanout setScanoutTwo {SCANOUT_ID_TWO, RESOURCE_ID_TWO, RESOURCE_WIDTH, RESOURCE_HEIGHT}; -+ virtioGpu::transferToHost2d transfer2HostTwo {RESOURCE_ID_TWO, RESOURCE_WIDTH, RESOURCE_HEIGHT}; -+ -+ std::vector backingPagesOne; -+ std::vector attachBackingMemOne; -+ virtioGpu::resourceAttachBacking* pAttachBackingOne; -+ virtioGpu::resourceMemEntry* pMemEntriesOne; -+ -+ std::vector backingPagesTwo; -+ std::vector attachBackingMemTwo; -+ virtioGpu::resourceAttachBacking* pAttachBackingTwo; -+ virtioGpu::resourceMemEntry* pMemEntriesTwo; -+ -+ const uint8_t FRAME_BYTE_ONE {0x55}; -+ const uint8_t FRAME_BYTE_TWO {0xaa}; -+ auto initializeBacking = [](std::vector& attachBackingMem, -+ std::vector& backingPages, -+ virtioGpu::resourceAttachBacking*& pAttachBacking, -+ virtioGpu::resourceMemEntry*& pMemEntries, -+ uint32_t uResourceId, uint8_t frame_byte) { -+ attachBackingMem.resize(ATTACH_BACKING_STRUCT_SIZE); -+ pAttachBacking = reinterpret_cast(attachBackingMem.data()); -+ pMemEntries = reinterpret_cast(attachBackingMem.data()+sizeof(virtioGpu::resourceAttachBacking)); -+ pAttachBacking->hdr.uType = virtioGpu::ctrlType::cmd::RESOURCE_ATTACH_BACKING; -+ pAttachBacking->uResourceId = uResourceId; -+ pAttachBacking->uNrEntries = NUM_BACKINGS; -+ -+ for (auto idx {0u}; idx < NUM_BACKINGS; idx++) { -+ void* backingPtr {RTMemAlloc(BACKING_SIZE)}; -+ backingPages.emplace_back(backingPtr); -+ -+ pMemEntries[idx].uAddr = reinterpret_cast(backingPtr); -+ pMemEntries[idx].uLength = BACKING_SIZE; -+ -+ std::memset(backingPages.at(idx), frame_byte, BACKING_SIZE); -+ } -+ }; -+ -+ initializeBacking(attachBackingMemOne, backingPagesOne, pAttachBackingOne, pMemEntriesOne, RESOURCE_ID_ONE, FRAME_BYTE_ONE); -+ initializeBacking(attachBackingMemTwo, backingPagesTwo, pAttachBackingTwo, pMemEntriesTwo, RESOURCE_ID_TWO, FRAME_BYTE_TWO); -+ -+ auto frameBufPage = [&displayManager](int32_t displayIdx, size_t idx) -> void* { -+ if (displayManager.display(displayIdx)->pFrameBuffer() == nullptr) { -+ return nullptr; -+ } -+ return reinterpret_cast(displayManager.display(displayIdx)->pFrameBuffer())+idx*BACKING_SIZE; -+ }; -+ -+ // Returns true if the framebuffer and the backing have the same values in them -+ auto compareFramebufBacking = [&frameBufPage](std::vector backingPages, uint32_t displayIdx) -> bool { -+ if (frameBufPage(displayIdx, 0) == nullptr) { -+ return false; -+ } -+ -+ std::vector results; -+ for (auto idx {0u}; idx < NUM_BACKINGS; idx++) { -+ results.emplace_back(std::memcmp(backingPages.at(idx), frameBufPage(displayIdx, idx), BACKING_SIZE)); -+ } -+ return std::all_of(std::begin(results), std::end(results), [](int res) { return res == 0; }); -+ }; -+ -+ runSimpleCommandAndCheck(&createResourceOne, &recvHdr); -+ runComplexCommandAndCheck(pAttachBackingOne, ATTACH_BACKING_STRUCT_SIZE, &recvHdr); -+ -+ runSimpleCommandAndCheck(&createResourceTwo, &recvHdr); -+ runComplexCommandAndCheck(pAttachBackingTwo, ATTACH_BACKING_STRUCT_SIZE, &recvHdr); -+ -+ /* -+ * TESTING - SINGLE MONITOR -+ */ -+ -+ WHEN("TRANSFER_2_HOST is called") { -+ runSimpleCommand(&transfer2HostOne, &recvHdr); -+ -+ THEN("The scanout is enabled and has its initial width and height") { -+ REQUIRE(displayManager.display(0)->fAttached == true); -+ REQUIRE(displayManager.display(0)->uCurrentWidth == virtioGpu::INITIAL_WIDTH); -+ REQUIRE(displayManager.display(0)->uCurrentHeight == virtioGpu::INITIAL_HEIGHT); -+ } -+ -+ AND_THEN("The transferring fails because no scanout is assigned to the resource") { -+ REQUIRE(recvHdr.hdr.uType == virtioGpu::ctrlType::resp::ERR_INVALID_RESOURCE_ID); -+ REQUIRE(not compareFramebufBacking(backingPagesOne, SCANOUT_ID_ONE)); -+ } -+ } -+ -+ WHEN("SET_SCANOUT is called with the resource Id of an existing resource") { -+ runSimpleCommandAndCheck(&setScanoutOne, &recvHdr); -+ -+ THEN("The scanout is enabled and has the given dimension") { -+ REQUIRE(displayManager.display(0)->fAttached == true); -+ REQUIRE(displayManager.display(0)->uCurrentWidth == RESOURCE_WIDTH); -+ REQUIRE(displayManager.display(0)->uCurrentHeight == RESOURCE_HEIGHT); -+ } -+ -+ AND_WHEN("SET_SCANOUT is called with a resource Id of 0") { -+ runSimpleCommand(&disableScanoutOne, &recvHdr); -+ -+ THEN("The scanout is disabled") { -+ REQUIRE(displayManager.display(0)->fAttached == false); -+ } -+ } -+ -+ AND_WHEN("TRANSFER_2_HOST ist called") { -+ runSimpleCommandAndCheck(&transfer2HostOne, &recvHdr); -+ -+ THEN("The transferring is successful") { -+ REQUIRE(compareFramebufBacking(backingPagesOne, SCANOUT_ID_ONE)); -+ } -+ } -+ -+ AND_WHEN("DETACH_BACKING and TRANSFER_2_HOST are called") { -+ // we again want to compare the framebuffer and the backing, thus we have to clear the framebuffer -+ std::memset(displayManager.display(0)->pFrameBuffer(), 0, displayManager.display(0)->cbFrameBuffer()); -+ -+ runSimpleCommandAndCheck(&detachBackingOne, &recvHdr); -+ runSimpleCommandAndCheck(&transfer2HostOne, &recvHdr); -+ -+ THEN("No data is transferred") { -+ REQUIRE(not compareFramebufBacking(backingPagesOne, SCANOUT_ID_ONE)); -+ } -+ } -+ -+ AND_THEN("GET_DISPLAY_INFO also reports the given resolution") { -+ virtioAdapter.prepareCommand(&getDisplayInfo, sizeof(getDisplayInfo), &recvDisplayInfo, virtioGpu::respDisplayInfo::size(TST_VIOGPU_MAX_SCANOUTS), virtioGpu::virtqIdx::CONTROLQ, &virtqBuf); -+ handler.handleBuffer(&virtqBuf); -+ REQUIRE(recvDisplayInfo.hdr.uType == virtioGpu::ctrlType::resp::OK_DISPLAY_INFO); -+ -+ REQUIRE(recvDisplayInfo.pmodes[0].enabled != 0); -+ REQUIRE(recvDisplayInfo.pmodes[0].r.width == RESOURCE_WIDTH); -+ REQUIRE(recvDisplayInfo.pmodes[0].r.height == RESOURCE_HEIGHT); -+ } -+ -+ AND_WHEN("requestResize is called because the screen has changed") { -+ handler.requestResize(0, RESIZED_WIDTH, RESIZED_HEIGHT); -+ -+ THEN("the display reports still the same size") { -+ REQUIRE(displayManager.display(0)->uCurrentWidth == RESOURCE_WIDTH); -+ REQUIRE(displayManager.display(0)->uCurrentHeight == RESOURCE_HEIGHT); -+ } -+ -+ AND_WHEN("GET_DISPLAY_INFO is used to receive the new resolution") { -+ runSimpleCommandAndCheck(&getDisplayInfo, &recvDisplayInfo, virtioGpu::ctrlType::resp::OK_DISPLAY_INFO); -+ -+ THEN("the driver receives the new resolution and the display reports the new size") { -+ REQUIRE(recvDisplayInfo.pmodes[0].enabled != 0); -+ REQUIRE(recvDisplayInfo.pmodes[0].r.width == RESIZED_WIDTH); -+ REQUIRE(recvDisplayInfo.pmodes[0].r.height == RESIZED_HEIGHT); -+ REQUIRE(displayManager.display(0)->uCurrentWidth == RESIZED_WIDTH); -+ REQUIRE(displayManager.display(0)->uCurrentHeight == RESIZED_HEIGHT); -+ } -+ } -+ } -+ } -+ -+ /* -+ * TESTING - MULTI MONITOR -+ */ -+ -+ // Mirroring -+ WHEN("A single framebuffer is linked to two monitors and TRANSFER_TO_HOST is called") { -+ virtioGpu::setScanout setScanoutTwo2One {SCANOUT_ID_TWO, RESOURCE_ID_ONE, RESOURCE_WIDTH, RESOURCE_HEIGHT}; -+ runSimpleCommandAndCheck(&setScanoutOne, &recvDisplayInfo); -+ runSimpleCommandAndCheck(&setScanoutTwo2One, &recvDisplayInfo); -+ -+ runSimpleCommandAndCheck(&transfer2HostOne, &recvHdr); -+ -+ THEN("Both scanouts have the same data in their framebuffers") { -+ REQUIRE(compareFramebufBacking(backingPagesOne, SCANOUT_ID_ONE)); -+ REQUIRE(compareFramebufBacking(backingPagesOne, SCANOUT_ID_TWO)); -+ } -+ } -+ -+ // Join Displays 1 -+ WHEN("Different framebuffers are linked to two monitors and TRANSFER_TO_HOST is called") { -+ runSimpleCommandAndCheck(&setScanoutOne, &recvDisplayInfo); -+ runSimpleCommandAndCheck(&setScanoutTwo, &recvDisplayInfo); -+ -+ runSimpleCommandAndCheck(&transfer2HostOne, &recvHdr); -+ runSimpleCommandAndCheck(&transfer2HostTwo, &recvHdr); -+ -+ THEN("Both scanouts have the expected data in their framebuffers") { -+ REQUIRE(compareFramebufBacking(backingPagesOne, SCANOUT_ID_ONE)); -+ REQUIRE(compareFramebufBacking(backingPagesTwo, SCANOUT_ID_TWO)); -+ } -+ } -+ } -+} -diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp -index d53ba59fcf..b0796c38b5 100644 ---- a/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp -+++ b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp -@@ -1457,6 +1457,18 @@ HRESULT showVMInfo(ComPtr pVirtualBox, - else - pszCtrl = "VBoxSVGA"; - break; -+ case GraphicsControllerType_VGAWithVirtioGpu: -+ if (details == VMINFO_MACHINEREADABLE) -+ pszCtrl = "vga-virtiogpu"; -+ else -+ pszCtrl = "VGAWithVirtioGPU"; -+ break; -+ case GraphicsControllerType_VirtioGpu: -+ if (details == VMINFO_MACHINEREADABLE) -+ pszCtrl = "virtiogpu"; -+ else -+ pszCtrl = "VirtioGPU"; -+ break; - default: - if (details == VMINFO_MACHINEREADABLE) - pszCtrl = "unknown"; -diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp -index aa3ecf7986..ce922b008c 100644 ---- a/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp -+++ b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp -@@ -1005,6 +1005,12 @@ RTEXITCODE handleModifyVM(HandlerArg *a) - || !RTStrICmp(ValueUnion.psz, "svga")) - CHECK_ERROR(pGraphicsAdapter, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VBoxSVGA)); - #endif -+ else if ( !RTStrICmp(ValueUnion.psz, "vga-virtiogpu")) { -+ CHECK_ERROR(pGraphicsAdapter, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VGAWithVirtioGpu)); -+ } -+ else if ( !RTStrICmp(ValueUnion.psz, "virtiogpu")) { -+ CHECK_ERROR(pGraphicsAdapter, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VirtioGpu)); -+ } - else - { - errorArgument(ModifyVM::tr("Invalid --graphicscontroller argument '%s'"), ValueUnion.psz); -diff --git a/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendCOM.cpp b/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendCOM.cpp -index 3799bcab84..f5da2c826a 100644 ---- a/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendCOM.cpp -+++ b/src/VBox/Frontends/VirtualBox/src/converter/UIConverterBackendCOM.cpp -@@ -316,6 +316,8 @@ template<> QString toString(const KGraphicsControllerType &type) - case KGraphicsControllerType_VBoxVGA: return QApplication::translate("UICommon", "VBoxVGA", "GraphicsControllerType"); - case KGraphicsControllerType_VMSVGA: return QApplication::translate("UICommon", "VMSVGA", "GraphicsControllerType"); - case KGraphicsControllerType_VBoxSVGA: return QApplication::translate("UICommon", "VBoxSVGA", "GraphicsControllerType"); -+ case KGraphicsControllerType_VGAWithVirtioGpu: return QApplication::translate("UICommon", "VGAWithVirtioGPU", "GraphicsControllerType"); -+ case KGraphicsControllerType_VirtioGpu: return QApplication::translate("UICommon", "VirtioGPU", "GraphicsControllerType"); - default: AssertMsgFailed(("No text for %d", type)); break; - } - return QString(); -@@ -329,6 +331,8 @@ template<> KGraphicsControllerType fromString(const QSt - list.insert(QApplication::translate("UICommon", "VBoxVGA", "GraphicsControllerType"), KGraphicsControllerType_VBoxVGA); - list.insert(QApplication::translate("UICommon", "VMSVGA", "GraphicsControllerType"), KGraphicsControllerType_VMSVGA); - list.insert(QApplication::translate("UICommon", "VBoxSVGA", "GraphicsControllerType"), KGraphicsControllerType_VBoxSVGA); -+ list.insert(QApplication::translate("UICommon", "VGAWithVirtioGPU", "GraphicsControllerType"), KGraphicsControllerType_VGAWithVirtioGpu); -+ list.insert(QApplication::translate("UICommon", "VirtioGPU", "GraphicsControllerType"), KGraphicsControllerType_VirtioGpu); - if (!list.contains(strType)) - { - AssertMsgFailed(("No value for '%s'", strType.toUtf8().constData())); -diff --git a/src/VBox/Main/idl/VirtualBox.xidl b/src/VBox/Main/idl/VirtualBox.xidl -index 26e9d94aa0..ad6b2dd395 100644 ---- a/src/VBox/Main/idl/VirtualBox.xidl -+++ b/src/VBox/Main/idl/VirtualBox.xidl -@@ -6536,6 +6536,12 @@ - - VirtualBox VGA device with VMware SVGA II extensions. - -+ -+ Virtualbox VGA device for the boot screen switching to Intel HD Graphics. -+ -+ -+ Intel HD Graphics with hardware acceleration. -+ - - - &ptrMachine, - const ComPtr &ptrGraphicsAdapter, - const ComPtr &ptrBiosSettings, -- bool fHMEnabled); -+ bool fHMEnabled, -+ bool fHideMultipleMonitors = false); - int i_checkMediumLocation(IMedium *pMedium, bool *pfUseHostIOCache); - int i_unmountMediumFromGuest(PUVM pUVM, PCVMMR3VTABLE pVMM, StorageBus_T enmBus, DeviceType_T enmDevType, - const char *pcszDevice, unsigned uInstance, unsigned uLUN, -@@ -808,6 +809,10 @@ private: - - HRESULT i_attachRawPCIDevices(PUVM pUVM, BusAssignmentManager *BusMgr, PCFGMNODE pDevices); - HRESULT i_attachVfioDevices(BusAssignmentManager *BusMgr, PCFGMNODE pDevices, PCVMMR3VTABLE pVMM); -+ HRESULT i_attachVirtioGpuDevice(BusAssignmentManager *BusMgr, -+ PCFGMNODE pDevices, -+ const ComPtr &ptrGraphicsAdapter, -+ bool secondaryController); - struct LEDSET; - typedef struct LEDSET *PLEDSET; - PPDMLED volatile *i_getLedSet(uint32_t iLedSet); -diff --git a/src/VBox/Main/src-client/BusAssignmentManager.cpp b/src/VBox/Main/src-client/BusAssignmentManager.cpp -index b4665d696c..1085b52865 100644 ---- a/src/VBox/Main/src-client/BusAssignmentManager.cpp -+++ b/src/VBox/Main/src-client/BusAssignmentManager.cpp -@@ -120,7 +120,9 @@ static const DeviceAssignmentRule g_aGenericRules[] = - {"vfio", 0, 16, 0, 1}, - {"vfio", 0, 17, 0, 1}, - {"vfio", 0, 18, 0, 1}, -- {"vfio", 0, 19, 0, 1}, -+ -+ /* Virtio-Gpu */ -+ {"virtio-gpu", 0, 19, 0, 1}, - - /* ISA/LPC controller */ - {"lpc", 0, 31, 0, 0}, -diff --git a/src/VBox/Main/src-client/ConsoleImpl2.cpp b/src/VBox/Main/src-client/ConsoleImpl2.cpp -index cc919754dc..112341cefe 100644 ---- a/src/VBox/Main/src-client/ConsoleImpl2.cpp -+++ b/src/VBox/Main/src-client/ConsoleImpl2.cpp -@@ -677,7 +677,7 @@ HRESULT Console::i_attachRawPCIDevices(PUVM pUVM, BusAssignmentManager *pBusMgr, - #endif - - --HRESULT Console::i_attachVfioDevices(BusAssignmentManager *pBusMgr, PCFGMNODE pDevices, PCVMMR3VTABLE pVMM) -+HRESULT Console::i_attachVfioDevices(BusAssignmentManager *pBusMgr, PCFGMNODE pDevices, PCVMMR3VTABLE /*pVMM*/) - { - HRESULT hrc {S_OK}; - PCFGMNODE pInst{NULL}; -@@ -737,6 +737,39 @@ HRESULT Console::i_attachVfioDevices(BusAssignmentManager *pBusMgr, PCFGMNODE pD - return hrc; - } - -+HRESULT Console::i_attachVirtioGpuDevice(BusAssignmentManager *pBusMgr, -+ PCFGMNODE pDevices, -+ const ComPtr &ptrGraphicsAdapter, -+ bool secondaryController) -+{ -+ PCFGMNODE pInst {NULL}; -+ PCFGMNODE pVirtioGpuDev {NULL}; -+ -+ InsertConfigNode(pDevices, "virtio-gpu", &pVirtioGpuDev); -+ InsertConfigNode(pVirtioGpuDev, "0", &pInst); -+ InsertConfigInteger(pInst, "Trusted", 1); -+ -+ PCFGMNODE pCfg {NULL}; -+ PCFGMNODE pLunL0 {NULL}; -+ -+ InsertConfigNode(pInst, "Config", &pCfg); -+ InsertConfigInteger(pCfg, "secondaryController", secondaryController); -+ -+ unsigned cMonitorCount {0}; -+ auto hrc = ptrGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitorCount); H(); -+ InsertConfigInteger(pCfg, "MonitorCount", cMonitorCount); -+ -+ unsigned cVRamMBs; -+ hrc = ptrGraphicsAdapter->COMGETTER(VRAMSize)(&cVRamMBs); H(); -+ InsertConfigInteger(pCfg, "VRamSize", cVRamMBs * _1M); -+ -+ InsertConfigNode(pInst, "LUN#0", &pLunL0); -+ InsertConfigString(pLunL0, "Driver", "MainDisplay"); -+ InsertConfigNode(pLunL0, "Config", &pCfg); -+ -+ return pBusMgr->assignPCIDevice("virtio-gpu", pInst); -+} -+ - /** - * Updates the device type for a LED. - * -@@ -1485,6 +1518,26 @@ int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, Au - bool fGimDebug = false; - com::Utf8Str strGimDebugAddress = "127.0.0.1"; - uint32_t uGimDebugPort = 50000; -+ -+ PCFGMNODE pHvNode; -+ InsertConfigNode(pParavirtNode, "HyperV", &pHvNode); -+ -+ { -+ ComPtr pGraphicsAdapter; -+ hrc = pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam()); H(); -+ GraphicsControllerType_T enmGraphicsController; -+ hrc = pGraphicsAdapter->COMGETTER(GraphicsControllerType)(&enmGraphicsController); H(); -+ -+ switch (enmGraphicsController) { -+ case GraphicsControllerType_VirtioGpu: -+ case GraphicsControllerType_VGAWithVirtioGpu: -+ InsertConfigInteger(pHvNode, "VirtioGPU", true); -+ break; -+ default: -+ break; -+ } -+ } -+ - if (strParavirtDebug.isNotEmpty()) - { - /* Hyper-V debug options. */ -@@ -1537,8 +1590,6 @@ int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, Au - /* Update HyperV CFGM node with active debug options. */ - if (fGimHvDebug) - { -- PCFGMNODE pHvNode; -- InsertConfigNode(pParavirtNode, "HyperV", &pHvNode); - InsertConfigString(pHvNode, "VendorID", strGimHvVendor); - InsertConfigInteger(pHvNode, "VSInterface", fGimHvVsIf ? 1 : 0); - InsertConfigInteger(pHvNode, "HypercallDebugInterface", fGimHvHypercallIf ? 1 : 0); -@@ -1987,6 +2038,23 @@ int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, Au - if (FAILED(vrc)) - return vrc; - break; -+ case GraphicsControllerType_VGAWithVirtioGpu: -+ hrc = i_attachVirtioGpuDevice(pBusMgr, pDevices, pGraphicsAdapter, true); -+ if (FAILED(hrc)) { -+ return hrc; -+ } -+ // See case GraphicsControllerType_VGAWithIntelGVT -+ hrc = i_configGraphicsController(pDevices, GraphicsControllerType_VBoxSVGA, pBusMgr, pMachine, pGraphicsAdapter, biosSettings, -+ RT_BOOL(fHMEnabled), true); -+ if (FAILED(hrc)) -+ return hrc; -+ break; -+ case GraphicsControllerType_VirtioGpu: -+ hrc = i_attachVirtioGpuDevice(pBusMgr, pDevices, pGraphicsAdapter, false); -+ if (FAILED(hrc)) { -+ return hrc; -+ } -+ break; - default: - AssertMsgFailed(("Invalid graphicsController=%d\n", enmGraphicsController)); - return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, -@@ -4387,7 +4455,8 @@ int Console::i_configGraphicsController(PCFGMNODE pDevices, - const ComPtr &ptrMachine, - const ComPtr &ptrGraphicsAdapter, - const ComPtr &ptrBiosSettings, -- bool fHMEnabled) -+ bool fHMEnabled, -+ bool fHideMultipleMonitors) - { - // InsertConfig* throws - try -@@ -4404,11 +4473,24 @@ int Console::i_configGraphicsController(PCFGMNODE pDevices, - - hrc = pBusMgr->assignPCIDevice(pcszDevice, pInst); H(); - InsertConfigNode(pInst, "Config", &pCfg); -- ULONG cVRamMBs; -- hrc = ptrGraphicsAdapter->COMGETTER(VRAMSize)(&cVRamMBs); H(); -- InsertConfigInteger(pCfg, "VRamSize", cVRamMBs * _1M); -+ ULONG cVRam; -+ hrc = ptrGraphicsAdapter->COMGETTER(VRAMSize)(&cVRam); H(); -+ InsertConfigInteger(pCfg, "VRamSize", cVRam * _1M); - ULONG cMonitorCount; -- hrc = ptrGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitorCount); H(); -+ -+ /** -+ * If the Virtio GPU is used with multiple monitors we hide additional -+ * monitors for the VirtualBox VGA adapter that is used for the legacy -+ * output, as the legacy output does not require multiple monitors. -+ * This leads to the multiple monitors appearing when we switch to the -+ * Virtio GPU. -+ */ -+ if (not fHideMultipleMonitors) -+ { -+ hrc = ptrGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitorCount); H(); -+ } else { -+ cMonitorCount = 1; -+ } - InsertConfigInteger(pCfg, "MonitorCount", cMonitorCount); - #ifdef VBOX_WITH_2X_4GB_ADDR_SPACE - InsertConfigInteger(pCfg, "R0Enabled", fHMEnabled); -diff --git a/src/VBox/Main/src-client/DisplayImpl.cpp b/src/VBox/Main/src-client/DisplayImpl.cpp -index 5a4beefebb..1f236d913d 100644 ---- a/src/VBox/Main/src-client/DisplayImpl.cpp -+++ b/src/VBox/Main/src-client/DisplayImpl.cpp -@@ -65,6 +65,9 @@ - # include - #endif - -+#include -+#include -+ - /** - * Display driver instance data. - * -@@ -2330,7 +2333,12 @@ HRESULT Display::drawToScreen(ULONG aScreenId, BYTE *aAddress, ULONG aX, ULONG a - - if ( !pFBInfo->fVBVAEnabled - && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN) -+ { -+ if (not pDisplay->mpDrv) { -+ return VINF_SUCCESS; -+ } - pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort, /* fFailOnResize = */ true); -+ } - else - { - if (!pFBInfo->fDisabled) -@@ -2664,6 +2672,39 @@ HRESULT Display::setScreenLayout(ScreenLayoutMode_T aScreenLayoutMode, - p->fDisplayFlags |= VMMDEV_DISPLAY_PRIMARY; - } - -+ auto virtioGpuEnabled = [this]() -> bool { -+ ComPtr pGraphicsAdapter; -+ HRESULT hrc = mParent->i_machine()->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam()); -+ AssertComRCReturnRC(hrc); -+ -+ GraphicsControllerType_T adapterType; -+ CHECK_ERROR(pGraphicsAdapter, COMGETTER(GraphicsControllerType)(&adapterType)); -+ return (adapterType == GraphicsControllerType_VGAWithVirtioGpu) or -+ (adapterType == GraphicsControllerType_VirtioGpu); -+ }; -+ -+ if (virtioGpuEnabled()) { -+ AutoReadLock alock2(this COMMA_LOCKVAL_SRC_POS); -+ Console::SafeVMPtr ptrVM(mParent); -+ if (!ptrVM.isOk()) { -+ LogRelFunc(("VMPtr is not okay.\n")); -+ return ptrVM.hrc(); -+ } -+ alock2.release(); -+ -+ PPDMIBASE pBase; -+ int rc = ptrVM.vtable()->pfnPDMR3QueryDevice(ptrVM.rawUVM(), "virtio-gpu", 0, &pBase); -+ AssertReleaseMsg(RT_SUCCESS(rc), ("query device failed")); -+ -+ PPDMIVIRTIOGPUPORT pPort {PDMIBASE_QUERY_INTERFACE(pBase, PDMIVIRTIOGPUPORT)}; -+ AssertMsg(pPort, ("Query interface failed.\n")); -+ AssertMsg(pPort->pfnDisplayChanged, ("No resize handler available.\n")); -+ PPDMDEVINS pDevIns = PDMIBASE_2_PDMDEV(pBase); -+ if (pPort->pfnDisplayChanged) { -+ pPort->pfnDisplayChanged(pDevIns, cDisplays, paDisplayDefs); -+ } -+ } -+ - bool const fForce = aScreenLayoutMode == ScreenLayoutMode_Reset - || aScreenLayoutMode == ScreenLayoutMode_Apply; - bool const fNotify = aScreenLayoutMode != ScreenLayoutMode_Silent; -diff --git a/src/VBox/Main/src-server/GraphicsAdapterImpl.cpp b/src/VBox/Main/src-server/GraphicsAdapterImpl.cpp -index 759f4324ff..481d18a50f 100644 ---- a/src/VBox/Main/src-server/GraphicsAdapterImpl.cpp -+++ b/src/VBox/Main/src-server/GraphicsAdapterImpl.cpp -@@ -198,6 +198,8 @@ HRESULT GraphicsAdapter::setGraphicsControllerType(GraphicsControllerType_T aGra - case GraphicsControllerType_VMSVGA: - case GraphicsControllerType_VBoxSVGA: - #endif -+ case GraphicsControllerType_VGAWithVirtioGpu: -+ case GraphicsControllerType_VirtioGpu: - break; - default: - return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType); -diff --git a/src/VBox/Main/src-server/SystemPropertiesImpl.cpp b/src/VBox/Main/src-server/SystemPropertiesImpl.cpp -index c238ae37cf..cfee49e932 100644 ---- a/src/VBox/Main/src-server/SystemPropertiesImpl.cpp -+++ b/src/VBox/Main/src-server/SystemPropertiesImpl.cpp -@@ -1687,6 +1687,8 @@ HRESULT SystemProperties::getSupportedGraphicsControllerTypes(std::vectorsetAttribute("controller", pcszGraphics); -diff --git a/src/VBox/Main/xml/VirtualBox-settings.xsd b/src/VBox/Main/xml/VirtualBox-settings.xsd -index 60f7303c5e..460c5e7907 100644 ---- a/src/VBox/Main/xml/VirtualBox-settings.xsd -+++ b/src/VBox/Main/xml/VirtualBox-settings.xsd -@@ -289,6 +289,8 @@ - - - -+ -+ - - - -diff --git a/src/VBox/Runtime/VBox/log-vbox.cpp b/src/VBox/Runtime/VBox/log-vbox.cpp -index 9d9c5d731e..5b75d76fbe 100644 ---- a/src/VBox/Runtime/VBox/log-vbox.cpp -+++ b/src/VBox/Runtime/VBox/log-vbox.cpp -@@ -273,6 +273,7 @@ RTDECL(PRTLOGGER) RTLogDefaultInit(void) - ASSERT_LOG_GROUP(DEV_SERIAL); - ASSERT_LOG_GROUP(DEV_SMC); - ASSERT_LOG_GROUP(DEV_VFIO); -+ ASSERT_LOG_GROUP(DEV_VIRTIO_GPU); - ASSERT_LOG_GROUP(DEV_VGA); - ASSERT_LOG_GROUP(DEV_VIRTIO); - ASSERT_LOG_GROUP(DEV_VIRTIO_NET); -diff --git a/src/VBox/VMM/VMMR3/GIMHv.cpp b/src/VBox/VMM/VMMR3/GIMHv.cpp -index 1da9065120..116045bb2f 100644 ---- a/src/VBox/VMM/VMMR3/GIMHv.cpp -+++ b/src/VBox/VMM/VMMR3/GIMHv.cpp -@@ -239,7 +239,8 @@ VMMR3_INT_DECL(int) gimR3HvInit(PVM pVM, PCFGMNODE pGimCfg) - int rc2 = CFGMR3ValidateConfig(pCfgHv, "/HyperV/", - "VendorID" - "|VSInterface" -- "|HypercallDebugInterface", -+ "|HypercallDebugInterface" -+ "|VirtioGPU", - "" /* pszValidNodes */, "GIM/HyperV" /* pszWho */, 0 /* uInstance */); - if (RT_FAILURE(rc2)) - return rc2; -@@ -368,6 +369,17 @@ VMMR3_INT_DECL(int) gimR3HvInit(PVM pVM, PCFGMNODE pGimCfg) - ; - #endif - -+ bool withVirtioGPU {false}; -+ rc = CFGMR3QueryBoolDef(pCfgHv, "VirtioGPU", &withVirtioGPU, false); -+ -+ if (RT_SUCCESS(rc) and withVirtioGPU and pVM->cCpus > 8) { -+ LogRel(("Disabling the GIM_HV_HINT_X2APIC_MSRS HyperV hint because there are %d virtual CPUs and " \ -+ "the VirtioGPU is used as a graphics controller. More than 8 vCPUs are known to result in " -+ "Windows10 not booting if the DVServerKMD driver is installed in the guest if the specific " -+ "HyperV hint is set.\n", pVM->cCpus)); -+ pHv->uHyperHints &= ~GIM_HV_HINT_X2APIC_MSRS; -+ } -+ - /* Partition features. */ - #ifdef VBOX_WITH_KVM - /* Extended hypercalls require KVM_EXIT_HYPER_HCALL exits to be forwarded gimHvHypercall. --- -2.43.0 -